diff options
author | Martin Sebor <msebor@redhat.com> | 2018-08-17 04:01:14 +0000 |
---|---|---|
committer | Jeff Law <law@gcc.gnu.org> | 2018-08-16 22:01:14 -0600 |
commit | bbcbd744b80c2cd40d7ef41d32dbd6ee7400701f (patch) | |
tree | 41aaf431ca5d1badf20f6186830991a73761cee6 /gcc/gimple-ssa-sprintf.c | |
parent | e584cd3509e56990aaf64b7e0b28881051483cb5 (diff) | |
download | gcc-bbcbd744b80c2cd40d7ef41d32dbd6ee7400701f.zip gcc-bbcbd744b80c2cd40d7ef41d32dbd6ee7400701f.tar.gz gcc-bbcbd744b80c2cd40d7ef41d32dbd6ee7400701f.tar.bz2 |
re PR tree-optimization/86853 (sprintf optimization for wide strings doesn't account for conversion failure)
gcc/ChangeLog:
PR tree-optimization/86853
* gimple-ssa-sprintf.c (struct format_result): Rename member.
(struct fmtresult): Add member and initialize it in ctors.
(format_character): Handle %C. Extend range to NUL. Set MAYFAIL.
(format_string): Handle %S the same as %ls. Set MAYFAIL.
(format_directive): Set POSUNDER4K when MAYFAIL is set.
(parse_directive): Handle %C same as %c.
(sprintf_dom_walker::compute_format_length): Adjust.
(is_call_safe): Adjust.
gcc/testsuite/ChangeLog:
PR tree-optimization/86853
* gcc.dg/tree-ssa/builtin-sprintf-10.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf-11.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf-warn-18.c: Adjust.
From-SVN: r263612
Diffstat (limited to 'gcc/gimple-ssa-sprintf.c')
-rw-r--r-- | gcc/gimple-ssa-sprintf.c | 73 |
1 files changed, 53 insertions, 20 deletions
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 2431b52..b5e1a08 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -211,12 +211,14 @@ struct format_result the return value optimization. */ bool knownrange; - /* True if no individual directive resulted in more than 4095 bytes - of output (the total NUMBER_CHARS_{MIN,MAX} might be greater). - Implementations are not required to handle directives that produce - more than 4K bytes (leading to undefined behavior) and so when one - is found it disables the return value optimization. */ - bool under4k; + /* True if no individual directive could fail or result in more than + 4095 bytes of output (the total NUMBER_CHARS_{MIN,MAX} might be + greater). Implementations are not required to handle directives + that produce more than 4K bytes (leading to undefined behavior) + and so when one is found it disables the return value optimization. + Similarly, directives that can fail (such as wide character + directives) disable the optimization. */ + bool posunder4k; /* True when a floating point directive has been seen in the format string. */ @@ -651,7 +653,7 @@ struct fmtresult fmtresult (unsigned HOST_WIDE_INT min = HOST_WIDE_INT_MAX) : argmin (), argmax (), knownrange (min < HOST_WIDE_INT_MAX), - nullp () + mayfail (), nullp () { range.min = min; range.max = min; @@ -665,7 +667,7 @@ struct fmtresult unsigned HOST_WIDE_INT likely = HOST_WIDE_INT_MAX) : argmin (), argmax (), knownrange (min < HOST_WIDE_INT_MAX && max < HOST_WIDE_INT_MAX), - nullp () + mayfail (), nullp () { range.min = min; range.max = max; @@ -695,6 +697,10 @@ struct fmtresult heuristics that depend on warning levels. */ bool knownrange; + /* True for a directive that may fail (such as wide character + directives). */ + bool mayfail; + /* True when the argument is a null pointer. */ bool nullp; }; @@ -2210,7 +2216,8 @@ format_character (const directive &dir, tree arg, vr_values *vr_values) res.knownrange = true; - if (dir.modifier == FMT_LEN_l) + if (dir.specifier == 'C' + || dir.modifier == FMT_LEN_l) { /* A wide character can result in as few as zero bytes. */ res.range.min = 0; @@ -2225,13 +2232,20 @@ format_character (const directive &dir, tree arg, vr_values *vr_values) res.range.likely = 0; res.range.unlikely = 0; } - else if (min > 0 && min < 128) + else if (min >= 0 && min < 128) { + /* Be conservative if the target execution character set + is not a 1-to-1 mapping to the source character set or + if the source set is not ASCII. */ + bool one_2_one_ascii + = (target_to_host_charmap[0] == 1 && target_to_host ('a') == 97); + /* A wide character in the ASCII range most likely results in a single byte, and only unlikely in up to MB_LEN_MAX. */ - res.range.max = 1; + res.range.max = one_2_one_ascii ? 1 : target_mb_len_max ();; res.range.likely = 1; res.range.unlikely = target_mb_len_max (); + res.mayfail = !one_2_one_ascii; } else { @@ -2240,6 +2254,8 @@ format_character (const directive &dir, tree arg, vr_values *vr_values) res.range.max = target_mb_len_max (); res.range.likely = 2; res.range.unlikely = res.range.max; + /* Converting such a character may fail. */ + res.mayfail = true; } } else @@ -2249,6 +2265,7 @@ format_character (const directive &dir, tree arg, vr_values *vr_values) res.range.max = target_mb_len_max (); res.range.likely = 2; res.range.unlikely = res.range.max; + res.mayfail = true; } } else @@ -2285,7 +2302,8 @@ format_string (const directive &dir, tree arg, vr_values *) /* A '%s' directive with a string argument with constant length. */ res.range = slen.range; - if (dir.modifier == FMT_LEN_l) + if (dir.specifier == 'S' + || dir.modifier == FMT_LEN_l) { /* In the worst case the length of output of a wide string S is bounded by MB_LEN_MAX * wcslen (S). */ @@ -2311,6 +2329,10 @@ format_string (const directive &dir, tree arg, vr_values *) /* Even a non-empty wide character string need not convert into any bytes. */ res.range.min = 0; + + /* A non-empty wide character conversion may fail. */ + if (slen.range.max > 0) + res.mayfail = true; } else { @@ -2349,7 +2371,8 @@ format_string (const directive &dir, tree arg, vr_values *) at level 2. This result is adjust upward for width (if it's specified). */ - if (dir.modifier == FMT_LEN_l) + if (dir.specifier == 'S' + || dir.modifier == FMT_LEN_l) { /* A wide character converts to as few as zero bytes. */ slen.range.min = 0; @@ -2361,6 +2384,10 @@ format_string (const directive &dir, tree arg, vr_values *) if (slen.range.likely < target_int_max ()) slen.range.unlikely *= target_mb_len_max (); + + /* A non-empty wide character conversion may fail. */ + if (slen.range.max > 0) + res.mayfail = true; } res.range = slen.range; @@ -2913,11 +2940,14 @@ format_directive (const sprintf_dom_walker::call_info &info, of 4095 bytes required to be supported? */ bool minunder4k = fmtres.range.min < 4096; bool maxunder4k = fmtres.range.max < 4096; - /* Clear UNDER4K in the overall result if the maximum has exceeded - the 4k (this is necessary to avoid the return valuye optimization + /* Clear POSUNDER4K in the overall result if the maximum has exceeded + the 4k (this is necessary to avoid the return value optimization that may not be safe in the maximum case). */ if (!maxunder4k) - res->under4k = false; + res->posunder4k = false; + /* Also clear POSUNDER4K if the directive may fail. */ + if (fmtres.mayfail) + res->posunder4k = false; if (!warned /* Only warn at level 2. */ @@ -3363,12 +3393,15 @@ parse_directive (sprintf_dom_walker::call_info &info, dir.fmtfunc = format_none; break; + case 'C': case 'c': + /* POSIX wide character and C/POSIX narrow character. */ dir.fmtfunc = format_character; break; case 'S': case 's': + /* POSIX wide string and C/POSIX narrow character string. */ dir.fmtfunc = format_string; break; @@ -3526,10 +3559,10 @@ sprintf_dom_walker::compute_format_length (call_info &info, res->range.min = res->range.max = 0; /* No directive has been seen yet so the length of output is bounded - by the known range [0, 0] (with no conversion producing more than - 4K bytes) until determined otherwise. */ + by the known range [0, 0] (with no conversion resulting in a failure + or producing more than 4K bytes) until determined otherwise. */ res->knownrange = true; - res->under4k = true; + res->posunder4k = true; res->floating = false; res->warned = false; @@ -3597,7 +3630,7 @@ is_call_safe (const sprintf_dom_walker::call_info &info, const format_result &res, bool under4k, unsigned HOST_WIDE_INT retval[2]) { - if (under4k && !res.under4k) + if (under4k && !res.posunder4k) return false; /* The minimum return value. */ |