Skip to content

Commit

Permalink
gcc/ChangeLog:
Browse files Browse the repository at this point in the history
	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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@263612 138bc75d-0d04-0410-961f-82ee72b054a4
  • Loading branch information
law committed Aug 17, 2018
1 parent 10f417f commit 17d7e9f
Show file tree
Hide file tree
Showing 6 changed files with 259 additions and 22 deletions.
12 changes: 12 additions & 0 deletions gcc/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
2018-08-16 Martin Sebor <msebor@redhat.com>

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.

2018-08-16 Bernd Edlinger <bernd.edlinger@hotmail.de>

* builtins.c (c_strlen): Add new parameter eltsize. Use it
Expand Down
73 changes: 53 additions & 20 deletions gcc/gimple-ssa-sprintf.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
};
Expand Down Expand Up @@ -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;
Expand All @@ -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
{
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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). */
Expand All @@ -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
{
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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. */
Expand Down
7 changes: 7 additions & 0 deletions gcc/testsuite/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
2018-08-16 Martin Sebor <msebor@redhat.com>

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.

2018-08-16 David Malcolm <dmalcolm@redhat.com>

* gcc.dg/missing-header-fixit-3.c: New test.
Expand Down
119 changes: 119 additions & 0 deletions gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-10.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/* PR tree-optimization/86853 - sprintf optimization for wide strings
doesn't account for conversion failure​
{ dg-do compile }
{ dg-options "-O2 -Wall -fdump-tree-optimized" } */

typedef __SIZE_TYPE__ size_t;
typedef __WCHAR_TYPE__ wchar_t;

extern int snprintf (char*, size_t, const char*, ...);

#define CONCAT(x, y) x ## y
#define CAT(x, y) CONCAT (x, y)
#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__)

#define FAIL(name) do { \
extern void FAILNAME (name) (void); \
FAILNAME (name)(); \
} while (0)

/* Macro to emit a call to funcation named
call_in_true_branch_not_eliminated_on_line_NNN()
for each call that's expected to be eliminated. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that no such call appears in output. */
#define ELIM(expr) \
if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0

/* Macro to emit a call to a function named
call_made_in_{true,false}_branch_on_line_NNN()
for each call that's expected to be retained. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that the expected number of both kinds of calls appears in output
(a pair for each line with the invocation of the KEEP() macro. */
#define KEEP(expr) \
if (expr) \
FAIL (made_in_true_branch); \
else \
FAIL (made_in_false_branch)


extern wchar_t wc;
extern wchar_t ws[];

const wchar_t ws3[] = L"12\xff";

/* Verify that the following calls are eliminated. */

void elim_wide_char_call (void)
{
ELIM (snprintf (0, 0, "%lc", L'\0'));
ELIM (snprintf (0, 0, "%lc", L'1'));
ELIM (snprintf (0, 0, "%lc", L'a'));
ELIM (snprintf (0, 0, "%lc", ws3[0]));
ELIM (snprintf (0, 0, "%lc", ws3[1]));
ELIM (snprintf (0, 0, "%lc", ws3[3]));

ELIM (snprintf (0, 0, "%C", L'\0'));
ELIM (snprintf (0, 0, "%C", L'9'));
ELIM (snprintf (0, 0, "%C", L'z'));
ELIM (snprintf (0, 0, "%C", ws3[0]));
ELIM (snprintf (0, 0, "%C", ws3[1]));
ELIM (snprintf (0, 0, "%C", ws3[3]));

/* Verify an unknown character value within the ASCII range. */
if (wc < 1 || 127 < wc)
wc = 0;

ELIM (snprintf (0, 0, "%C", wc));
ELIM (snprintf (0, 0, "%C", wc));
}

void elim_wide_string_call (void)
{
ELIM (snprintf (0, 0, "%ls", L""));
}


#line 1000

/* Verify that the following calls are not eliminated. */

void keep_wide_char_call (void)
{
KEEP (snprintf (0, 0, "%lc", L'\xff'));
KEEP (snprintf (0, 0, "%lc", L'\xffff'));
KEEP (snprintf (0, 0, "%lc", wc));
KEEP (snprintf (0, 0, "%lc", ws3[2]));

KEEP (snprintf (0, 0, "%C", L'\xff'));
KEEP (snprintf (0, 0, "%C", L'\xffff'));
KEEP (snprintf (0, 0, "%C", wc));
KEEP (snprintf (0, 0, "%C", ws3[2]));

/* Verify an unknown character value outside the ASCII range
(with 128 being the only one). */
if (wc < 32 || 128 < wc)
wc = 32;

KEEP (snprintf (0, 0, "%lc", wc));
KEEP (snprintf (0, 0, "%C", wc));
}

void keep_wide_string_call (void)
{
KEEP (snprintf (0, 0, "%ls", L"\xff"));
KEEP (snprintf (0, 0, "%ls", L"\xffff"));
KEEP (snprintf (0, 0, "%ls", ws));
KEEP (snprintf (0, 0, "%ls", ws3));

KEEP (snprintf (0, 0, "%S", L"\xff"));
KEEP (snprintf (0, 0, "%S", L"\xffff"));
KEEP (snprintf (0, 0, "%S", ws));
KEEP (snprintf (0, 0, "%S", ws3));
}

/* { dg-final { scan-tree-dump-times "call_made_in_true_branch_not_eliminated" 0 "optimized" } }
{ dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 18 "optimized" } }
{ dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 18 "optimized" } } */
Loading

0 comments on commit 17d7e9f

Please sign in to comment.