aboutsummaryrefslogtreecommitdiff
path: root/gcc/gimple-ssa-sprintf.c
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2018-08-17 04:01:14 +0000
committerJeff Law <law@gcc.gnu.org>2018-08-16 22:01:14 -0600
commitbbcbd744b80c2cd40d7ef41d32dbd6ee7400701f (patch)
tree41aaf431ca5d1badf20f6186830991a73761cee6 /gcc/gimple-ssa-sprintf.c
parente584cd3509e56990aaf64b7e0b28881051483cb5 (diff)
downloadgcc-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.c73
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. */