From: Martin Sebor Date: Fri, 3 Feb 2017 22:47:35 +0000 (+0000) Subject: PR tree-optimization/79327 - wrong code at -O2 and -fprintf-return-value X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=f589a1ce25d8320f4dc552b91949181edd616b64;p=gcc.git PR tree-optimization/79327 - wrong code at -O2 and -fprintf-return-value PR tree-optimization/79327 - wrong code at -O2 and -fprintf-return-value gcc/ChangeLog: * gimple-ssa-sprintf.c (tree_digits): Avoid adding the base prefix when precision has resulted in leading zeros. (format_integer): Adjust the likely counter to assume an unknown argument that may be zero is non-zero. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Adjust. * gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-13.c: New test. * gcc/testsuite/gcc.dg/tree-ssa/pr79327-2.c: Ditto. From-SVN: r245173 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 044f720e990..40993e5a0dd 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,11 @@ +2017-02-03 Martin Sebor + + PR tree-optimization/79327 + * gimple-ssa-sprintf.c (tree_digits): Avoid adding the base prefix + when precision has resulted in leading zeros. + (format_integer): Adjust the likely counter to assume an unknown + argument that may be zero is non-zero. + 2017-02-03 Jason Merrill PR c++/78689 diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index ac4e959ed3c..e6cc31d1c48 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -762,7 +762,9 @@ tree_digits (tree x, int base, HOST_WIDE_INT prec, bool plus, bool prefix) res += prec < ndigs ? ndigs : prec; - if (prefix && absval) + /* Adjust a non-zero value for the base prefix, either hexadecimal, + or, unless precision has resulted in a leading zero, also octal. */ + if (prefix && absval && (base == 16 || prec <= ndigs)) { if (base == 8) res += 1; @@ -1230,6 +1232,10 @@ format_integer (const directive &dir, tree arg) of the format string by returning [-1, -1]. */ return fmtresult (); + /* True if the LIKELY counter should be adjusted upward from the MIN + counter to account for arguments with unknown values. */ + bool likely_adjust = false; + fmtresult res; /* Using either the range the non-constant argument is in, or its @@ -1259,6 +1265,14 @@ format_integer (const directive &dir, tree arg) res.argmin = argmin; res.argmax = argmax; + + /* Set the adjustment for an argument whose range includes + zero since that doesn't include the octal or hexadecimal + base prefix. */ + wide_int wzero = wi::zero (wi::get_precision (min)); + if (wi::le_p (min, wzero, SIGNED) + && !wi::neg_p (max)) + likely_adjust = true; } else if (range_type == VR_ANTI_RANGE) { @@ -1293,6 +1307,11 @@ format_integer (const directive &dir, tree arg) if (!argmin) { + /* Set the adjustment for an argument whose range includes + zero since that doesn't include the octal or hexadecimal + base prefix. */ + likely_adjust = true; + if (TREE_CODE (argtype) == POINTER_TYPE) { argmin = build_int_cst (pointer_sized_int_node, 0); @@ -1345,7 +1364,24 @@ format_integer (const directive &dir, tree arg) res.range.max = MAX (max1, max2); } - res.range.likely = res.knownrange ? res.range.max : res.range.min; + /* Add the adjustment for an argument whose range includes zero + since it doesn't include the octal or hexadecimal base prefix. */ + if (res.knownrange) + res.range.likely = res.range.max; + else + { + res.range.likely = res.range.min; + if (likely_adjust && maybebase && base != 10) + { + if (res.range.min == 1) + res.range.likely += base == 8 ? 1 : 2; + else if (res.range.min == 2 + && base == 16 + && (dir.width[0] == 2 || dir.prec[0] == 2)) + ++res.range.likely; + } + } + res.range.unlikely = res.range.max; res.adjust_for_width_or_precision (dir.width, dirtype, base, (sign | maybebase) + (base == 16)); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7a34cfeb6db..73696e5b6cc 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2017-02-03 Martin Sebor + + PR tree-optimization/79327 + * gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Adjust. + * gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-13.c: New test. + * gcc/testsuite/gcc.dg/tree-ssa/pr79327-2.c: Ditto. + 2017-02-03 Jakub Jelinek Martin Sebor diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c index fef9578921d..b4a9a6e61b6 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c @@ -1151,8 +1151,16 @@ void test_sprintf_chk_hh_nonconst (int w, int p, int a) T (2, "% hhu", a); /* { dg-warning ". . flag used with .%u." } */ T (2, "% hhx", a); /* { dg-warning ". . flag used with .%x." } */ - T (2, "%#hho", a); - T (2, "%#hhx", a); + /* The following results in between "0" and "0377" for -1. Although + the minimum output would fit, given the '#' flag the likely output + (i.e., for any non-zero argument) includes a leading zero followed + by one or more octal digits, which results in the terminating nul + being written past the end. Thus the "may write" warning. */ + T (2, "%#hho", a); /* { dg-warning "may write a terminating nul" } */ + /* Similar to the above, but the likely output of the directive for + a non-zero argument overflows. Thus the "writing X bytes" (as + opposed to "may write") warning. */ + T (2, "%#hhx", a); /* { dg-warning "writing between 1 and 4 bytes" } */ T (3, "%0hhd", a); T (3, "%1hhd", a); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-13.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-13.c new file mode 100644 index 00000000000..27a99729867 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-13.c @@ -0,0 +1,261 @@ +/* { dg-do compile } + { dg-options "-O2 -Wall -Wformat-overflow=1 -ftrack-macro-expansion=0" } + { dg-require-effective-target int32plus } */ + +typedef __SIZE_TYPE__ size_t; +typedef __WCHAR_TYPE__ wchar_t; + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-INT_MAX - 1) + +/* When debugging, define LINE to the line number of the test case to exercise + and avoid exercising any of the others. The buffer and objsize macros + below make use of LINE to avoid warnings for other lines. */ +#ifndef LINE +# define LINE 0 +#endif + +void sink (char*, char*); + +int dummy_sprintf (char*, const char*, ...); + +char buffer [256]; +extern char *ptr; + +int int_range (int min, int max) +{ + extern int int_value (void); + int n = int_value (); + return n < min || max < n ? min : n; +} + +unsigned uint_range (unsigned min, unsigned max) +{ + extern unsigned uint_value (void); + unsigned n = uint_value (); + return n < min || max < n ? min : n; +} + +/* Evaluate to an array of SIZE characters when non-negative, or to + a pointer to an unknown object otherwise. */ +#define buffer(size) \ + ((0 <= size) ? buffer + sizeof buffer - (size) : ptr) + +/* Helper to expand function to either __builtin_f or dummy_f to + make debugging GCC easy. */ +#define FUNC(f) \ + ((!LINE || LINE == __LINE__) ? __builtin_ ## f : dummy_ ## f) + +/* Macro to verify that calls to __builtin_sprintf (i.e., with no size + argument) issue diagnostics by correctly determining the size of + the destination buffer. */ +#define T(size, ...) \ + (FUNC (sprintf) (buffer (size), __VA_ARGS__), \ + sink (buffer, ptr)) + +/* Return a signed integer in the range [MIN, MAX]. */ +#define R(min, max) int_range (min, max) + +/* Verify warnings and ranges for certain overflow. */ +void test_min_overflow (int i) +{ + T (0, "%#hho", i); /* { dg-warning "between 1 and 4 bytes" } */ + T (0, "%#1hho", i); /* { dg-warning "between 1 and 4 bytes" } */ + T (0, "%#2hho", i); /* { dg-warning "between 2 and 4 bytes" } */ + T (0, "%#3hho", i); /* { dg-warning "between 3 and 4 bytes" } */ + T (0, "%#4hho", i); /* { dg-warning "writing 4 bytes" } */ + T (0, "%#hho", R (-1, 0)); /* { dg-warning "between 1 and 4 bytes" } */ + T (0, "%#1hho", R (-1, 0)); /* { dg-warning "between 1 and 4 bytes" } */ + T (0, "%#2hho", R (-1, 0)); /* { dg-warning "between 2 and 4 bytes" } */ + T (0, "%#3hho", R (-1, 0)); /* { dg-warning "between 3 and 4 bytes" } */ + T (0, "%#4hho", R (-1, 0)); /* { dg-warning "writing 4 bytes" } */ + T (0, "%#hho", R (-1, 1)); /* { dg-warning "between 1 and 4 bytes" } */ + T (0, "%#1hho", R (-1, 1)); /* { dg-warning "between 1 and 4 bytes" } */ + T (0, "%#2hho", R (-1, 1)); /* { dg-warning "between 2 and 4 bytes" } */ + T (0, "%#3hho", R (-1, 1)); /* { dg-warning "between 3 and 4 bytes" } */ + T (0, "%#4hho", R (-1, 1)); /* { dg-warning "writing 4 bytes" } */ + T (0, "%#hho", R ( 0, 1)); /* { dg-warning "between 1 and 2 bytes" } */ + T (0, "%#1hho", R ( 0, 1)); /* { dg-warning "between 1 and 2 bytes" } */ + T (0, "%#2hho", R ( 0, 1)); /* { dg-warning "writing 2 bytes" } */ + T (0, "%#3hho", R ( 0, 1)); /* { dg-warning "writing 3 bytes" } */ + T (0, "%#4hho", R ( 0, 1)); /* { dg-warning "writing 4 bytes" } */ + T (0, "%#hho", R ( 1, 2)); /* { dg-warning "writing 2 bytes" } */ + T (0, "%#1hho", R ( 1, 2)); /* { dg-warning "writing 2 bytes" } */ + T (0, "%#2hho", R ( 1, 2)); /* { dg-warning "writing 2 bytes" } */ + T (0, "%#3hho", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */ + T (0, "%#4hho", R ( 1, 2)); /* { dg-warning "writing 4 bytes" } */ + + T (0, "%#ho", i); /* { dg-warning "between 1 and 7 bytes" } */ + T (0, "%#.*ho", /* { dg-warning "between 1 and 7 bytes" } */ + R (0, 2), i); + T (0, "%#.*ho", /* { dg-warning "between 1 and 7 bytes" } */ + R (1, 2), i); + T (0, "%#.*ho", /* { dg-warning "between 2 and 7 bytes" } */ + R (2, 3), i); + T (0, "%#.*ho", /* { dg-warning "between 3 and 7 bytes" } */ + R (3, 4), i); + T (0, "%#.*ho", /* { dg-warning "between 7 and 8 bytes" } */ + R (7, 8), i); + + T (0, "%#ho", R (-1, 0)); /* { dg-warning "between 1 and 7 bytes" } */ + T (0, "%#ho", R (-1, 1)); /* { dg-warning "between 1 and 7 bytes" } */ + T (0, "%#ho", R ( 0, 1)); /* { dg-warning "between 1 and 2 bytes" } */ + T (0, "%#ho", R ( 1, 2)); /* { dg-warning "writing 2 bytes" } */ + + T (0, "%#o", i); /* { dg-warning "between 1 and 12 bytes" } */ + T (0, "%#o", R (-1, 0)); /* { dg-warning "between 1 and 12 bytes" } */ + T (0, "%#o", R (-1, 1)); /* { dg-warning "between 1 and 12 bytes" } */ + T (0, "%#o", R ( 0, 1)); /* { dg-warning "between 1 and 2 bytes" } */ + T (0, "%#o", R ( 1, 2)); /* { dg-warning "writing 2 bytes" } */ + + T (0, "%#hhx", i); /* { dg-warning "between 1 and 4 bytes" } */ + T (0, "%#.*hhx", /* { dg-warning "writing up to 4 bytes" } */ + R (0, 2), i); + T (0, "%#.*hhx", /* { dg-warning "between 1 and 4 bytes" } */ + R (1, 2), i); + T (0, "%#.*hhx", /* { dg-warning "between 2 and 5 bytes" } */ + R (2, 3), i); + T (0, "%#.*hhx", /* { dg-warning "between 3 and 6 bytes" } */ + R (3, 4), i); + + T (0, "%#hhx", R (-1, 0)); /* { dg-warning "between 1 and 4 bytes" } */ + T (0, "%#hhx", R (-1, 1)); /* { dg-warning "between 1 and 4 bytes" } */ + T (0, "%#hhx", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */ + T (0, "%#hhx", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */ + + T (0, "%#hx", i); /* { dg-warning "between 1 and 6 bytes" } */ + T (0, "%#hx", R (-1, 0)); /* { dg-warning "between 1 and 6 bytes" } */ + T (0, "%#hx", R (-1, 1)); /* { dg-warning "between 1 and 6 bytes" } */ + T (0, "%#hx", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */ + T (0, "%#hx", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */ + + T (0, "%#x", i); /* { dg-warning "between 1 and 10 bytes" } */ + T (0, "%#x", R (-1, 0)); /* { dg-warning "between 1 and 10 bytes" } */ + T (0, "%#x", R (-1, 1)); /* { dg-warning "between 1 and 10 bytes" } */ + T (0, "%#x", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */ + T (0, "%#x", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */ +} + +/* Verify warnings and ranges for likely overflow. */ +void test_likely_overflow (int i) +{ + T (2, "%#hho", i); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#1hho", i); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#2hho", i); /* { dg-warning "writing a terminating nul" } */ + T (2, "%#3hho", i); /* { dg-warning "between 3 and 4 bytes" } */ + T (2, "%#4hho", i); /* { dg-warning "writing 4 bytes" } */ + T (2, "%#hho", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#1hho", R (-1, 0));/* { dg-warning "may write a terminating nul" } */ + T (2, "%#2hho", R (-1, 0));/* { dg-warning "writing a terminating nul" } */ + T (2, "%#3hho", R (-1, 0));/* { dg-warning "between 3 and 4 bytes" } */ + T (2, "%#4hho", R (-1, 0));/* { dg-warning "writing 4 bytes" } */ + T (2, "%#hho", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#1hho", R (-1, 1));/* { dg-warning "may write a terminating nul" } */ + T (2, "%#2hho", R (-1, 1));/* { dg-warning "writing a terminating nul" } */ + T (2, "%#3hho", R (-1, 1));/* { dg-warning "between 3 and 4 bytes" } */ + T (2, "%#4hho", R (-1, 1));/* { dg-warning "writing 4 bytes" } */ + T (2, "%#hho", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#1hho", R ( 0, 1));/* { dg-warning "may write a terminating nul" } */ + T (2, "%#2hho", R ( 0, 1));/* { dg-warning "writing a terminating nul" } */ + T (2, "%#3hho", R ( 0, 1));/* { dg-warning "writing 3 bytes" } */ + T (2, "%#4hho", R ( 0, 1));/* { dg-warning "writing 4 bytes" } */ + T (2, "%#hho", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */ + T (2, "%#1hho", R ( 1, 2));/* { dg-warning "writing a terminating nul" } */ + T (2, "%#2hho", R ( 1, 2));/* { dg-warning "writing a terminating nul" } */ + T (2, "%#3hho", R ( 1, 2));/* { dg-warning "writing 3 bytes" } */ + T (2, "%#4hho", R ( 1, 2));/* { dg-warning "writing 4 bytes" } */ + + T (2, "%#ho", i); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#ho", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#ho", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#ho", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#ho", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */ + + T (2, "%#o", i); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#o", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#o", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#o", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */ + T (2, "%#o", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */ + + T (2, "%#hhx", i); /* { dg-warning "between 1 and 4 bytes" } */ + T (2, "%#1hhx", i); /* { dg-warning "between 1 and 4 bytes" } */ + T (2, "%#2hhx", i); /* { dg-warning "between 2 and 4 bytes" } */ + T (2, "%#3hhx", i); /* { dg-warning "between 3 and 4 bytes" } */ + T (2, "%#4hhx", i); /* { dg-warning "writing 4 bytes" } */ + T (2, "%#1hhx", R (-1, 0));/* { dg-warning "between 1 and 4 bytes" } */ + T (2, "%#2hhx", R (-1, 0));/* { dg-warning "between 2 and 4 bytes" } */ + T (2, "%#3hhx", R (-1, 0));/* { dg-warning "between 3 and 4 bytes" } */ + T (2, "%#4hhx", R (-1, 0));/* { dg-warning "writing 4 bytes" } */ + T (2, "%#hhx", R (-1, 0)); /* { dg-warning "between 1 and 4 bytes" } */ + T (2, "%#1hhx", R (-1, 0));/* { dg-warning "between 1 and 4 bytes" } */ + T (2, "%#2hhx", R (-1, 0));/* { dg-warning "between 2 and 4 bytes" } */ + T (2, "%#3hhx", R (-1, 0));/* { dg-warning "between 3 and 4 bytes" } */ + T (2, "%#4hhx", R (-1, 0));/* { dg-warning "writing 4 bytes" } */ + T (2, "%#hhx", R (-1, 1)); /* { dg-warning "between 1 and 4 bytes" } */ + T (2, "%#1hhx", R (-1, 1));/* { dg-warning "between 1 and 4 bytes" } */ + T (2, "%#2hhx", R (-1, 1));/* { dg-warning "between 2 and 4 bytes" } */ + T (2, "%#3hhx", R (-1, 1));/* { dg-warning "between 3 and 4 bytes" } */ + T (2, "%#4hhx", R (-1, 1));/* { dg-warning "writing 4 bytes" } */ + T (2, "%#hhx", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */ + T (2, "%#1hhx", R ( 0, 1));/* { dg-warning "between 1 and 3 bytes" } */ + T (2, "%#2hhx", R ( 0, 1));/* { dg-warning "between 2 and 3 bytes" } */ + T (2, "%#3hhx", R ( 0, 1));/* { dg-warning "writing 3 bytes" } */ + T (2, "%#4hhx", R ( 0, 1));/* { dg-warning "writing 4 bytes" } */ + T (2, "%#hhx", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */ + T (2, "%#1hhx", R ( 1, 2));/* { dg-warning "writing 3 bytes" } */ + T (2, "%#2hhx", R ( 1, 2));/* { dg-warning "writing 3 bytes" } */ + T (2, "%#3hhx", R ( 1, 2));/* { dg-warning "writing 3 bytes" } */ + T (2, "%#4hhx", R ( 1, 2));/* { dg-warning "writing 4 bytes" } */ + + T (2, "%#hx", i); /* { dg-warning "between 1 and 6 bytes" } */ + T (2, "%#hx", R (-1, 0)); /* { dg-warning "between 1 and 6 bytes" } */ + T (2, "%#hx", R (-1, 1)); /* { dg-warning "between 1 and 6 bytes" } */ + T (2, "%#hx", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */ + T (2, "%#hx", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */ + + T (2, "%#x", i); /* { dg-warning "between 1 and 10 bytes" } */ + T (2, "%#x", R (-1, 0)); /* { dg-warning "between 1 and 10 bytes" } */ + T (2, "%#x", R (-1, 1)); /* { dg-warning "between 1 and 10 bytes" } */ + T (2, "%#x", R ( 0, 1)); /* { dg-warning "between 1 and 3 bytes" } */ + T (2, "%#x", R ( 1, 2)); /* { dg-warning "writing 3 bytes" } */ +} + +/* Verify warnings likely overflow due to the terminating nul. */ +void test_likely_nul_overflow (int i) +{ + T (3, "%#hho", i); + T (3, "%#hho", R (-1, 0)); + T (3, "%#hho", R (-1, 1)); + T (3, "%#hho", R ( 0, 1)); + T (3, "%#hho", R ( 1, 2)); + + T (3, "%#ho", i); + T (3, "%#ho", R (-1, 0)); + T (3, "%#ho", R (-1, 1)); + T (3, "%#ho", R ( 0, 1)); + T (3, "%#ho", R ( 1, 2)); + + T (3, "%#o", i); + T (3, "%#o", R (-1, 0)); + T (3, "%#o", R (-1, 1)); + T (3, "%#o", R ( 0, 1)); + T (3, "%#o", R ( 1, 2)); + + T (3, "%#hhx", i); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#hhx", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#hhx", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#hhx", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#hhx", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */ + + T (3, "%#hx", i); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#hx", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#hx", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#hx", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#hx", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */ + + T (3, "%#x", i); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#x", R (-1, 0)); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#x", R (-1, 1)); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#x", R ( 0, 1)); /* { dg-warning "may write a terminating nul" } */ + T (3, "%#x", R ( 1, 2)); /* { dg-warning "writing a terminating nul" } */ +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79327-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79327-2.c new file mode 100644 index 00000000000..f72e4cc353c --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr79327-2.c @@ -0,0 +1,159 @@ +/* PR tree-optimization/79327 - wrong code at -O2 and -fprintf-return-value + { dg-compile } + { dg-options "-O2 -fdump-tree-optimized" } */ + +#define CAT(s, n) s ## n +#define FAIL(line) CAT (failure_on_line_, line) + +/* Emit a call to a function named failure_on_line_NNN when EXPR is false. */ +#define ASSERT(expr) \ + do { \ + extern void FAIL (__LINE__)(void); \ + if (!(expr)) FAIL (__LINE__)(); \ + } while (0) + +#define KEEP(line) CAT (keep_call_on_line_, line) + +/* Emit a call to a function named keep_call_on_line_NNN when EXPR is true. + Used to verify that the expression need not be the only one that holds. */ +#define ASSERT_MAYBE(expr) \ + do { \ + extern void KEEP (__LINE__)(void); \ + if (expr) KEEP (__LINE__)(); \ + } while (0) + +void test_hho_cst (void) +{ + ASSERT (1 == __builtin_snprintf (0, 0, "%#.1hho", 0)); + ASSERT (2 == __builtin_snprintf (0, 0, "%#.2hho", 0)); + + ASSERT (2 == __builtin_snprintf (0, 0, "%#.1hho", 1)); + ASSERT (2 == __builtin_snprintf (0, 0, "%#.2hho", 1)); + ASSERT (3 == __builtin_snprintf (0, 0, "%#.3hho", 1)); +} + +int test_hho_var (int i) +{ + int n = __builtin_snprintf (0, 0, "%#hho", i); + + ASSERT (0 < n && n < 5); + + ASSERT_MAYBE (1 == n); + ASSERT_MAYBE (2 == n); + ASSERT_MAYBE (3 == n); + ASSERT_MAYBE (4 == n); + + return n; +} + +int test_ho_var (int i) +{ + int n = __builtin_snprintf (0, 0, "%#ho", i); + + ASSERT (0 < n && n < 8); + + ASSERT_MAYBE (1 == n); + ASSERT_MAYBE (2 == n); + ASSERT_MAYBE (3 == n); + ASSERT_MAYBE (4 == n); + ASSERT_MAYBE (5 == n); + ASSERT_MAYBE (6 == n); + ASSERT_MAYBE (7 == n); + + return n; +} + +int test_o_var (int i) +{ + int n = __builtin_snprintf (0, 0, "%#o", i); + + ASSERT (0 < n && n < 13); + + ASSERT_MAYBE (1 == n); + ASSERT_MAYBE (2 == n); + ASSERT_MAYBE (3 == n); + ASSERT_MAYBE (4 == n); + ASSERT_MAYBE (5 == n); + ASSERT_MAYBE (6 == n); + ASSERT_MAYBE (7 == n); + ASSERT_MAYBE (8 == n); + ASSERT_MAYBE (9 == n); + ASSERT_MAYBE (10 == n); + ASSERT_MAYBE (11 == n); + ASSERT_MAYBE (12 == n); + + return n; +} + +void test_hhx_cst (void) +{ + ASSERT (1 == __builtin_snprintf (0, 0, "%#.1hhx", 0)); + ASSERT (2 == __builtin_snprintf (0, 0, "%#.2hhx", 0)); + + ASSERT (3 == __builtin_snprintf (0, 0, "%#.1hhx", 1)); + ASSERT (4 == __builtin_snprintf (0, 0, "%#.2hhx", 1)); + ASSERT (5 == __builtin_snprintf (0, 0, "%#.3hhx", 1)); +} + +int test_hhx_var (int i) +{ + int n = __builtin_snprintf (0, 0, "%#hhx", i); + + ASSERT (0 < n && n < 5); + + ASSERT_MAYBE (1 == n); + ASSERT_MAYBE (2 == n); + ASSERT_MAYBE (3 == n); + ASSERT_MAYBE (4 == n); + + return n; +} + +void test_hx_cst (void) +{ + ASSERT (1 == __builtin_snprintf (0, 0, "%#.1hx", 0)); + ASSERT (2 == __builtin_snprintf (0, 0, "%#.2hx", 0)); + + ASSERT (3 == __builtin_snprintf (0, 0, "%#.1hx", 1)); + ASSERT (4 == __builtin_snprintf (0, 0, "%#.2hx", 1)); + ASSERT (5 == __builtin_snprintf (0, 0, "%#.3hx", 1)); +} + +int test_hx_var (int i) +{ + int n = __builtin_snprintf (0, 0, "%#hx", i); + + ASSERT (0 < n && n < 7); + + ASSERT_MAYBE (1 == n); + ASSERT_MAYBE (2 == n); + ASSERT_MAYBE (3 == n); + ASSERT_MAYBE (4 == n); + ASSERT_MAYBE (5 == n); + ASSERT_MAYBE (6 == n); + + return n; +} + +int test_x_var (int i) +{ + int n = __builtin_snprintf (0, 0, "%#x", i); + + ASSERT (0 < n && n < 11); + + ASSERT_MAYBE (1 == n); + ASSERT_MAYBE (2 == n); + ASSERT_MAYBE (3 == n); + ASSERT_MAYBE (4 == n); + ASSERT_MAYBE (5 == n); + ASSERT_MAYBE (6 == n); + ASSERT_MAYBE (7 == n); + ASSERT_MAYBE (8 == n); + ASSERT_MAYBE (9 == n); + ASSERT_MAYBE (10 == n); + + return n; +} + +/* { dg-final { scan-tree-dump-not "failure_on_line" "optimized"} } + { dg-final { scan-tree-dump-times "keep_call_on_line" 43 "optimized"} } */