return true;
}
@@ -1260,10 +1248,8 @@ format_integer (const directive &dir, tr
enum value_range_type range_type = get_range_info (arg, &min, &max);
if (range_type == VR_RANGE)
{
- argmin = build_int_cst (argtype, wi::fits_uhwi_p (min)
- ? min.to_uhwi () : min.to_shwi ());
- argmax = build_int_cst (argtype, wi::fits_uhwi_p (max)
- ? max.to_uhwi () : max.to_shwi ());
+ argmin = wide_int_to_tree (argtype, min);
+ argmax = wide_int_to_tree (argtype, max);
/* Set KNOWNRANGE if the argument is in a known subrange
of the directive's type (KNOWNRANGE may be reset below). */
@@ -1307,47 +1293,8 @@ format_integer (const directive &dir, tr
if (!argmin)
{
- /* For an unknown argument (e.g., one passed to a vararg function)
- or one whose value range cannot be determined, create a T_MIN
- constant if the argument's type is signed and T_MAX otherwise,
- and use those to compute the range of bytes that the directive
- can output. When precision may be zero, use zero as the minimum
- since it results in no bytes on output (unless width is specified
- to be greater than 0). */
- bool zero = dir.prec[0] <= 0 && dir.prec[1] >= 0;
- argmin = build_int_cst (argtype, !zero);
-
- int typeprec = TYPE_PRECISION (dirtype);
- int argprec = TYPE_PRECISION (argtype);
-
- if (argprec < typeprec)
- {
- if (POINTER_TYPE_P (argtype))
- argmax = build_all_ones_cst (argtype);
- else if (TYPE_UNSIGNED (argtype))
- argmax = TYPE_MAX_VALUE (argtype);
- else
- argmax = TYPE_MIN_VALUE (argtype);
- }
- else
- {
- if (POINTER_TYPE_P (dirtype))
- argmax = build_all_ones_cst (dirtype);
- else if (TYPE_UNSIGNED (dirtype))
- argmax = TYPE_MAX_VALUE (dirtype);
- else
- argmax = TYPE_MIN_VALUE (dirtype);
- }
-
- res.argmin = argmin;
- res.argmax = argmax;
- }
-
- if (tree_int_cst_lt (argmax, argmin))
- {
- tree tmp = argmax;
- argmax = argmin;
- argmin = tmp;
+ argmin = TYPE_MIN_VALUE (argtype);
+ argmax = TYPE_MAX_VALUE (argtype);
}
/* Clear KNOWNRANGE if the range has been adjusted to the maximum
@@ -1361,34 +1308,33 @@ format_integer (const directive &dir, tr
res.argmax = argmax;
}
- /* Recursively compute the minimum and maximum from the known range,
- taking care to swap them if the lower bound results in longer
- output than the upper bound (e.g., in the range [-1, 0]. */
-
- if (TYPE_UNSIGNED (dirtype))
- {
- /* For unsigned conversions/directives, use the minimum (i.e., 0
- or 1) and maximum to compute the shortest and longest output,
- respectively. */
+ /* Recursively compute the minimum and maximum from the known range. */
+ if (TYPE_UNSIGNED (dirtype) || tree_int_cst_sgn (argmin) >= 0)
+ {
+ /* For unsigned conversions/directives or signed when
+ the minimum is positive, use the minimum and maximum to compute
+ the shortest and longest output, respectively. */
res.range.min = format_integer (dir, argmin).range.min;
res.range.max = format_integer (dir, argmax).range.max;
}
- else
+ else if (tree_int_cst_sgn (argmax) < 0)
{
- /* For signed conversions/directives, use the maximum (i.e., 0)
- to compute the shortest output and the minimum (i.e., TYPE_MIN)
- to compute the longest output. This is important when precision
- is specified but unknown because otherwise both output lengths
- would reflect the largest possible precision (i.e., INT_MAX). */
+ /* For signed conversions/directives if maximum is negative,
+ use the minimum as the longest output and maximum as the
+ shortest output. */
res.range.min = format_integer (dir, argmax).range.min;
res.range.max = format_integer (dir, argmin).range.max;
}
-
- if (res.range.max < res.range.min)
+ else
{
- unsigned HOST_WIDE_INT tmp = res.range.max;
- res.range.max = res.range.min;
- res.range.min = tmp;
+ /* Otherwise, 0 is inside of the range and minimum negative. Use 0
+ as the shortest output and for the longest output compute the
+ length of the output of both minimum and maximum and pick the
+ longer. */
+ unsigned HOST_WIDE_INT max1 = format_integer (dir, argmin).range.max;
+ unsigned HOST_WIDE_INT max2 = format_integer (dir, argmax).range.max;
+ res.range.min = format_integer (dir, integer_zero_node).range.min;
+ res.range.max = MAX (max1, max2);
}
res.range.likely = res.knownrange ? res.range.max : res.range.min;
--- gcc/testsuite/gcc.dg/tree-ssa/pr79327.c.jj 2017-02-02 08:56:42.278163030
+0100
+++ gcc/testsuite/gcc.dg/tree-ssa/pr79327.c 2017-02-02 08:56:42.278163030
+0100
@@ -0,0 +1,31 @@
+/* PR tree-optimization/79327 - wrong code at -O2 and -fprintf-return-value
+ { dg-do run }
+ { dg-options "-O2 -Wall" } */
+
+volatile int a, b = -1;
+char buf[64];
+
+#define FMT "%+03d%02d"
+const char* fmt = FMT;
+
+int main ()
+{
+ int c = a;
+ int d = b;
+ if (c >= -35791395 && c < 35791394 && d >= -1 && d < __INT_MAX__)
+ {
+ /* In the following the range of return values can be computed
+ by GCC. */
+ int n1 = __builtin_sprintf (buf, FMT, c + 1, d + 1);
+ if (n1 > 7)
+ __builtin_abort ();
+
+ /* Here GCC can't see the format string so the return value
+ must be computed by a libc call. */
+ int n2 = __builtin_sprintf (buf, fmt, c + 1, d + 1);
+
+ if (n1 != n2)
+ __builtin_abort ();
+ }
+ return 0;
+}
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-12.c.jj 2017-02-02
08:56:42.278163030 +0100
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-12.c 2017-02-02
08:56:42.278163030 +0100
@@ -0,0 +1,228 @@
+/* PR tree-optimization79/327 - wrong code at -O2 and -fprintf-return-value
+ { 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)
+
+/* Return a unsigned integer in the range [MIN, MAX]. */
+#define U(min, max) uint_range (min, max)
+
+/* Exercise the hh length modifier with ranges. */
+void test_hh (void)
+{
+ T (0, "%hhi", R ( -1, 0)); /* { dg-warning "between 1 and 2 bytes" }
*/
+ T (0, "%hhi", R ( -1, 1)); /* { dg-warning "between 1 and 2 bytes" }
*/
+ T (0, "%hhi", R ( -1, 12)); /* { dg-warning "between 1 and 2 bytes" }
*/
+ T (0, "%hhi", R ( -1, 123)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhi", R ( -1, 128)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R ( -1, 257)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R ( -1, 1234)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R ( -12, -11)); /* { dg-warning "writing 3 bytes" } */
+ T (0, "%hhi", R ( -12, -1)); /* { dg-warning "between 2 and 3 bytes" }
*/
+ T (0, "%hhi", R ( -12, 0)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhi", R ( -12, 1)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhi", R ( -12, 12)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhi", R ( -12, 123)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhi", R ( -12, 128)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R ( -12, 257)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R ( -12, 1234)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R ( -99, -10)); /* { dg-warning "writing 3 bytes" } */
+ T (0, "%hhi", R (-123, -1)); /* { dg-warning "between 2 and 4 bytes" }
*/
+ T (0, "%hhi", R (-123, 0)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R (-123, 1)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R (-123, 12)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R (-123, 123)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R (-123, 257)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R (-123, 1234)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R (-129, 1)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hhi", R (-130, -129)); /* { dg-warning "writing 3 bytes" } */
+
+ T (0, "%hhi", U ( 0, 127)); /* { dg-warning "between 1 and 3 bytes" }
*/
+
+ /* The following results in either "127" and "-128" so the ideal result
+ should be "between 3 and 4 bytes" but because of the overflow from
+ 128 to -128 in the %hhi directive the input range is reset to that
+ of char, or [CHAR_MIN, CHAR_MAX], and the warning reflects that. */
+ T (0, "%hhi", U ( 127, 128)); /* { dg-warning "between \[13\] and 4
bytes" } */
+ /* The following results in either "-128" or "-127". */
+ T (0, "%hhi", U ( 128, 129)); /* { dg-warning "writing 4 bytes" } */
+ /* The following results in between "-128" and "-99". */
+ T (0, "%hhi", U ( 128, 157)); /* { dg-warning "writing between 3 and 4
bytes" } */
+ /* Between "-128" and "-1". */
+ T (0, "%hhi", U ( 128, 255)); /* { dg-warning "writing between 2 and 4
bytes" } */
+ /* Between "-128" and "0". */
+ T (0, "%hhi", U ( 128, 256)); /* { dg-warning "writing between 1 and 4
bytes" } */
+ /* Between "-128" and "" (zero formats as nothing with zero precision). */
+ T (0, "%.0hhi", U ( 128, 256)); /* { dg-warning "writing up to 4 bytes" }
*/
+ /* Same as above but with a range of precisions including zero. */
+ T (0, "%.*hhi", /* { dg-warning "writing up to 4 bytes" }
*/
+ R (0, 1), U ( 128, 256));
+ /* Same as above but with a positive range of precisions. */
+ T (0, "%.*hhi", /* { dg-warning "between 1 and 4 bytes" }
*/
+ R (1, 2), U ( 128, 256));
+ /* Precision range includes zero but width is non-zero so output cannot
+ be empty. */
+ T (0, "%1.*hhi", /* { dg-warning "between 1 and 4 bytes" }
*/
+ R (0, 2), U ( 128, 256));
+ /* Same as above but with a width range. */
+ T (0, "%*.*hhi", /* { dg-warning "between 1 and 4 bytes" }
*/
+ R (1, 2), R (0, 2), U ( 128, 256));
+ /* Same as above but this time width range does include zero. */
+ T (0, "%*.*hhi", /* { dg-warning "up to 4 bytes" } */
+ R (0, 2), R (0, 2), U ( 128, 256));
+
+ /* Range of precisions in excess of the number of digits and sign. */
+ T (0, "%.*hhi", /* { dg-warning "between 5 and 8 bytes" }
*/
+ R (5, 7), U ( 128, 256));
+
+ /* Between "-128" and "+0". */
+ T (0, "%+hhi", U ( 128, 256)); /* { dg-warning "between 2 and 4 bytes" }
*/
+ /* Between "-128" and " 0". */
+ T (0, "% hhi", U ( 128, 256)); /* { dg-warning "between 2 and 4 bytes" }
*/
+
+ T (0, "%hhu", R ( -1, 1)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R ( -1, 12)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R ( -1, 123)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R ( -1, 128)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R ( -1, 257)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R ( -1, 1234)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R ( -12, 1)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R ( -12, 12)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R ( -12, 123)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R ( -12, 128)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R ( -12, 257)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R ( -12, 1234)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R (-123, 1)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R (-123, 12)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R (-123, 123)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R (-123, 257)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R (-123, 1234)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R (-129, 1)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hhu", R (-199, -159)); /* { dg-warning "writing 2 bytes" } */
+ T (0, "%hhu", R (-255, -250)); /* { dg-warning "writing 1 byte" } */
+}
+
+/* Exercise the h length modifier. */
+void test_h (void)
+{
+ T (0, "%hi", R ( -1, 0)); /* { dg-warning "between 1 and 2 bytes" }
*/
+ T (0, "%hi", R ( 0, 1)); /* { dg-warning "writing 1 byte" } */
+ T (0, "%hi", R ( -1, 1)); /* { dg-warning "between 1 and 2 bytes" }
*/
+ T (0, "%hi", R ( -1, 12)); /* { dg-warning "between 1 and 2 bytes" }
*/
+ T (0, "%hi", R ( -12, 1)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%hi", R ( -99, -10)); /* { dg-warning "writing 3 bytes" } */
+ T (0, "%hi", R ( -123, 4)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%hi", R ( -1234, 56)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%hi", R ( -1234, 567)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%hi", R ( -1234, 5678)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%ho", R (-32768,-32767)); /* { dg-warning "writing 6 bytes" } */
+
+ T (0, "%ho", R ( -1, 0)); /* { dg-warning "between 1 and 6 bytes" }
*/
+ T (0, "%ho", R ( 0, 1)); /* { dg-warning "writing 1 byte" } */
+ T (0, "%ho", R ( -1, 1)); /* { dg-warning "between 1 and 6 bytes" }
*/
+ T (0, "%ho", R ( -1, 12)); /* { dg-warning "between 1 and 6 bytes" }
*/
+ T (0, "%ho", R ( -12, 1)); /* { dg-warning "between 1 and 6 bytes" }
*/
+ T (0, "%ho", R ( -123, 4)); /* { dg-warning "between 1 and 6 bytes" }
*/
+ T (0, "%ho", R ( -1234, 56)); /* { dg-warning "between 1 and 6 bytes" }
*/
+ T (0, "%ho", R ( -1234, 567)); /* { dg-warning "between 1 and 6 bytes" }
*/
+ T (0, "%ho", R ( -1234, 5678)); /* { dg-warning "between 1 and 6 bytes" }
*/
+ T (0, "%ho", R (-32768,-32767)); /* { dg-warning "writing 6 bytes" } */
+
+ T (0, "%hu", R ( -1, 0)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%hu", R ( 0, 1)); /* { dg-warning "writing 1 byte" } */
+ T (0, "%hu", R ( -1, 1)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%hu", R ( -1, 12)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%hu", R ( -12, 1)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%hu", R ( -123, 4)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%hu", R ( -1234, 56)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%hu", R ( -1234, 567)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%hu", R ( -1234, 5678)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%hu", R (-32768,-32767)); /* { dg-warning "writing 5 bytes" } */
+
+ T (0, "%hx", R (-32768,-32767)); /* { dg-warning "writing 4 bytes" } */
+}
+
+/* Exercise integer directives with no length modifier. */
+void test_diou (void)
+{
+ T (0, "%d", R ( -1, 0)); /* { dg-warning "between 1 and 2 bytes" }
*/
+ T (0, "%i", R ( 0, 1)); /* { dg-warning "writing 1 byte" } */
+ T (0, "%d", R ( -1, 1)); /* { dg-warning "between 1 and 2 bytes" }
*/
+ T (0, "%i", R ( -1, 12)); /* { dg-warning "between 1 and 2 bytes" }
*/
+ T (0, "%d", R ( -12, 1)); /* { dg-warning "between 1 and 3 bytes" }
*/
+ T (0, "%i", R ( -123, 4)); /* { dg-warning "between 1 and 4 bytes" }
*/
+ T (0, "%d", R ( -1234, 56)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%i", R ( -1234, 567)); /* { dg-warning "between 1 and 5 bytes" }
*/
+ T (0, "%d", R ( -1234, 5678)); /* { dg-warning "between 1 and 5 bytes" }
*/
+
+ T (0, "%u", R ( -1, 0)); /* { dg-warning "between 1 and 10 bytes" }
*/
+ T (0, "%u", R ( 0, 1)); /* { dg-warning "writing 1 byte" } */
+ T (0, "%u", R ( -1, 1)); /* { dg-warning "between 1 and 10 bytes" }
*/
+ T (0, "%u", R ( -1, 12)); /* { dg-warning "between 1 and 10 bytes" }
*/
+ T (0, "%u", R ( -12, 1)); /* { dg-warning "between 1 and 10 bytes" }
*/
+ T (0, "%u", R ( -123, 4)); /* { dg-warning "between 1 and 10 bytes" }
*/
+ T (0, "%u", R ( -1234, 56)); /* { dg-warning "between 1 and 10 bytes" }
*/
+ T (0, "%u", R ( -1234, 567)); /* { dg-warning "between 1 and 10 bytes" }
*/
+ T (0, "%u", R ( -1234, 5678)); /* { dg-warning "between 1 and 10 bytes" }
*/
+
+ T (0, "%o", R ( -1, 0)); /* { dg-warning "between 1 and 11 bytes" }
*/
+ T (0, "%o", R ( -2, 1)); /* { dg-warning "between 1 and 11 bytes" }
*/
+ T (0, "%o", R ( 0, 1)); /* { dg-warning "writing 1 byte" } */
+
+ T (0, "%x", R ( -1, 0)); /* { dg-warning "between 1 and 8 bytes" } */
+ T (0, "%x", R ( -2, 1)); /* { dg-warning "between 1 and 8 bytes" } */
+ T (0, "%x", R ( 0, 1)); /* { dg-warning "writing 1 byte" } */
+}
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c.jj 2017-01-30
09:31:45.000000000 +0100
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c 2017-02-02
10:21:22.014902923 +0100
@@ -1151,8 +1151,8 @@ void test_sprintf_chk_hh_nonconst (int w
T (2, "% hhu", a); /* { dg-warning ". . flag used with .%u." } */
T (2, "% hhx", a); /* { dg-warning ". . flag used with .%x." } */
- T (2, "%#hho", a); /* { dg-warning "nul past the end" } */
- T (2, "%#hhx", a); /* { dg-warning ".%#hhx. directive writing between
3 and . bytes into a region of size 2" } */
+ T (2, "%#hho", a);
+ T (2, "%#hhx", a);
T (3, "%0hhd", a);
T (3, "%1hhd", a);
Jakub