1 /* PR middle-end/78476 - snprintf(0, 0, ...) with known arguments not
2    optimized away
3    PR middle-end/78512 - r242674 miscompiles Linux kernel
4    A negative test complementing builtin-sprintf-5.c to verify that calls
5    to the function that do not return a constant are not optimized away.
6    Test also verifies that unknown directives prevent the optimization.
7    { dg-do compile }
8    { dg-options "-O2 -Wformat -fdump-tree-optimized" }
9    { dg-require-effective-target int32plus } */
10 
11 typedef __SIZE_TYPE__ size_t;
12 
13 #define CONCAT(a, b) a ## b
14 #define CAT(a, b)    CONCAT (a, b)
15 
16 #define T(...)								\
17   do {									\
18     int CAT (n, __LINE__) = __builtin_snprintf (0, 0, __VA_ARGS__);	\
19     sink (CAT (n, __LINE__));						\
20   } while (0)
21 
22 void sink (int);
23 
24 static int
int_range(int min,int max)25 int_range (int min, int max)
26 {
27   extern int int_value (void);
28   int val = int_value ();
29   if (val < min || max < val)
30     val = min;
31   return val;
32 }
33 
34 #define R(min, max) int_range (min, max)
35 
test_arg_int(int width,int prec,int i,int n)36 void test_arg_int (int width, int prec, int i, int n)
37 {
38   T ("%i", i);
39   T ("%1i", i);
40   T ("%2i", i);
41   T ("%3i", i);
42   T ("%4i", i);
43 
44   T ("%*i", width, 0);
45   T ("%*i", width, 1);
46   T ("%*i", width, i);
47 
48   T ("%.*i", prec, 0);
49   T ("%.*i", prec, 1);
50   T ("%.*i", prec, i);
51   T ("%.*i", 0,    i);
52 
53   T ("%i", R (1, 10));
54 
55   /* Each of the bounds of the ranges below results in just one byte
56      on output because they convert to the value 9 in type char, yet
57      other values in those ranges can result in up to four bytes.
58      For example, 4240 converts to -112.  Verify that the output
59      isn't folded into a constant.  This assumes __CHAR_BIT__ of 8.  */
60   T ("%hhi", R (4104, 4360) + 1);
61   T ("%hhu", R (4104, 4360) + 1);
62 
63   /* Here, the range includes all possible lengths of output for
64      a 16-bit short and 32-bit int.  */
65   T ("%hi", R (65536, 65536 * 2));
66   T ("%hu", R (65536, 65536 * 2));
67 
68   T ("%'i", 1234567);
69 
70   for (i = -n; i != n; ++i)
71     T ("%*x", n, i);
72 }
73 
74 /* Support for %p was removed in response to PR middle-end/78512 due
75    to the Linux kernel relying on GCC builtins while at the same time
76    providing a large number of extensions to the %p directive that
77    interfere with the optimization.  Verify that %p disables it.  */
78 
test_arg_ptr(int width,int prec,int i)79 void test_arg_ptr (int width, int prec, int i)
80 {
81   T ("%p", (void*)0);
82   T ("p=%p", (void*)0);
83   T ("%s=%p", "p=", (void*)0);
84   T ("%i%p", 123, (void*)0);
85 }
86 
test_arg_string(int width,int prec,const char * s)87 void test_arg_string (int width, int prec, const char *s)
88 {
89   T ("%-s", s);
90   T ("%1s", s);
91   T ("%.1s", s);
92   T ("%*s", width, s);
93   T ("%.*s", prec, s);
94   T ("%1.*s", prec, s);
95   T ("%*.1s", width, s);
96   T ("%*.*s", width, prec, s);
97   T ("%*s", width, "123");
98   T ("%.*s", prec, "123");
99   T ("%1.*s", prec, "123");
100   T ("%*.1s", width, "123");
101   T ("%*.*s", width, prec, "123");
102 }
103 
test_invalid_directive(void)104 void test_invalid_directive (void)
105 {
106   T ("%");        /* { dg-warning "spurious trailing .%." } */
107   T ("abc%");     /* { dg-warning "spurious trailing .%." } */
108 
109   T ("%2$i");     /* { dg-warning "operand number out of range" } */
110   T ("abc%2$i");  /* { dg-warning "operand number out of range" } */
111 
112   T ("%=i", 0);   /* { dg-warning "unknown conversion type character .=." } */
113   /* { dg-warning "too many arguments" "" { target *-*-* } .-1 } */
114 
115   T ("%*i", "", 0); /* { dg-warning "field width specifier .\\*. expects argument of type .int." } */
116   T ("%.*i", "", 0); /* { dg-warning "field precision specifier .\\.\\*. expects argument of type .int." } */
117   T ("%.*.i", 0);   /* { dg-warning "unknown conversion type character .\\.." } */
118   T ("%Q");       /* { dg-warning "unknown conversion type character .Q." } */
119   T ("abc%Q");    /* { dg-warning "unknown conversion type character .Q." } */
120 }
121 
122 
123 /* Use 'grep "^ *T (" builtin-sprintf-6.c  | wc -l' to determine
124    the count for the directive below.
125    { dg-final { scan-tree-dump-times "snprintf" 46 "optimized"} } */
126