PR middle-end/49905 - Better sanity checking on sprintf src & dest to

gcc/ChangeLog:

	PR middle-end/49905
	* Makefile.in (OBJS): Add gimple-ssa-sprintf.o.
	* config/linux.h (TARGET_PRINTF_POINTER_FORMAT): Redefine.
	* config/linux.c (gnu_libc_printf_pointer_format): New function.
	* config/sol2.h (TARGET_PRINTF_POINTER_FORMAT): Same.
	* config/sol2.c (solaris_printf_pointer_format): New function.
	* doc/invoke.texi (-Wformat-length, -fprintf-return-value): New
	options.
	* doc/tm.texi.in (TARGET_PRINTF_POINTER_FORMAT): Document.
	* doc/tm.texi: Regenerate.
	* gimple-fold.h (get_range_strlen): New function.
	(get_maxval_strlen): Declare existing function.
	* gimple-fold.c (get_range_strlen): Add arguments and compute both
	maximum and minimum.
	 (get_range_strlen): Define overload.
	(get_maxval_strlen): Adjust.
	* gimple-ssa-sprintf.c: New file and pass.
	* passes.def (pass_sprintf_length): Add new pass.
	* targhooks.h (default_printf_pointer_format): Declare new function.
	(gnu_libc_printf_pointer_format): Same.
	(solaris_libc_printf_pointer_format): Same.
	* targhooks.c (default_printf_pointer_format): Define new function.
	* tree-pass.h (make_pass_sprintf_length): Declare new function.
	* print-tree.c: Increase buffer size.

gcc/c-family/ChangeLog:

	PR middle-end/49905
	* c.opt: Add -Wformat-length and -fprintf-return-value.

gcc/testsuite/ChangeLog:

	PR middle-end/49905
	* gcc.dg/builtin-stringop-chk-1.c: Adjust.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: New test.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-2.c: New test.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: New test.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-4.c: New test.
	* gcc.dg/tree-ssa/builtin-sprintf.c: New test.
	* gcc.dg/tree-ssa/builtin-sprintf-2.c: New test.

From-SVN: r240298
This commit is contained in:
Martin Sebor 2016-09-21 01:39:27 +00:00 committed by Martin Sebor
parent 6283a8db1f
commit 88d0c3f0a1
28 changed files with 5683 additions and 30 deletions

View File

@ -1,3 +1,30 @@
2016-09-20 Martin Sebor <msebor@redhat.com>
PR middle-end/49905
* Makefile.in (OBJS): Add gimple-ssa-sprintf.o.
* config/linux.h (TARGET_PRINTF_POINTER_FORMAT): Redefine.
* config/linux.c (gnu_libc_printf_pointer_format): New function.
* config/sol2.h (TARGET_PRINTF_POINTER_FORMAT): Same.
* config/sol2.c (solaris_printf_pointer_format): New function.
* doc/invoke.texi (-Wformat-length, -fprintf-return-value): New
options.
* doc/tm.texi.in (TARGET_PRINTF_POINTER_FORMAT): Document.
* doc/tm.texi: Regenerate.
* gimple-fold.h (get_range_strlen): New function.
(get_maxval_strlen): Declare existing function.
* gimple-fold.c (get_range_strlen): Add arguments and compute both
maximum and minimum.
(get_range_strlen): Define overload.
(get_maxval_strlen): Adjust.
* gimple-ssa-sprintf.c: New file and pass.
* passes.def (pass_sprintf_length): Add new pass.
* targhooks.h (default_printf_pointer_format): Declare new function.
(gnu_libc_printf_pointer_format): Same.
(solaris_libc_printf_pointer_format): Same.
* targhooks.c (default_printf_pointer_format): Define new function.
* tree-pass.h (make_pass_sprintf_length): Declare new function.
* print-tree.c: Increase buffer size.
2016-09-21 Kugan Vivekanandarajah <kuganv@linaro.org>
* tree-vrp.c (get_value_range): Teach PARM_DECL to use ipa-vrp

View File

@ -1296,6 +1296,7 @@ OBJS = \
gimple-ssa-nonnull-compare.o \
gimple-ssa-split-paths.o \
gimple-ssa-strength-reduction.o \
gimple-ssa-sprintf.o \
gimple-streamer-in.o \
gimple-streamer-out.o \
gimple-walk.o \

View File

@ -1,3 +1,8 @@
2016-09-20 Martin Sebor <msebor@redhat.com>
PR middle-end/49905
* c.opt: Add -Wformat-length and -fprintf-return-value.
2016-09-19 Bernd Edlinger <bernd.edlinger@hotmail.de>
PR c++/77434

View File

@ -478,6 +478,11 @@ Wformat-extra-args
C ObjC C++ ObjC++ Var(warn_format_extra_args) Warning LangEnabledBy(C ObjC C++ ObjC++,Wformat=, warn_format >= 1, 0)
Warn if passing too many arguments to a function for its format string.
Wformat-length
C ObjC C++ ObjC++ Warning Alias(Wformat-length=, 1, 0)
Warn about function calls with format strings that write past the end
of the destination region. Same as -Wformat-length=1.
Wformat-nonliteral
C ObjC C++ ObjC++ Var(warn_format_nonliteral) Warning LangEnabledBy(C ObjC C++ ObjC++,Wformat=, warn_format >= 2, 0)
Warn about format strings that are not literals.
@ -502,6 +507,11 @@ Wformat=
C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_format) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall, 1, 0)
Warn about printf/scanf/strftime/strfmon format string anomalies.
Wformat-length=
C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_format_length) Warning LangEnabledBy(C ObjC C++ ObjC++,Wformat=, warn_format >= 1, 0)
Warn about function calls with format strings that write past the end
of the destination region.
Wignored-qualifiers
C C++ Var(warn_ignored_qualifiers) Warning EnabledBy(Wextra)
Warn whenever type qualifiers are ignored.
@ -1487,6 +1497,10 @@ fpretty-templates
C++ ObjC++ Var(flag_pretty_templates) Init(1)
-fno-pretty-templates Do not pretty-print template specializations as the template signature followed by the arguments.
fprintf-return-value
C ObjC C++ ObjC++ LTO Optimization Var(flag_printf_return_value) Init(1)
Treat known sprintf return values as constants.
freplace-objc-classes
ObjC ObjC++ LTO Var(flag_replace_objc_classes)
Used in Fix-and-Continue mode to indicate that object files may be swapped in at runtime.

View File

@ -21,8 +21,12 @@ along with GCC; see the file COPYING3. If not see
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "linux-protos.h"
#undef TARGET_PRINTF_POINTER_FORMAT
#define TARGET_PRINTF_POINTER_FORMAT gnu_libc_printf_pointer_format
bool
linux_libc_has_function (enum function_class fn_class)
{
@ -36,3 +40,16 @@ linux_libc_has_function (enum function_class fn_class)
return false;
}
/* Glibc formats pointers as if by "%zx" except for the null pointer
which outputs "(nil)". It ignores the pound ('#') format flag but
interprets the space and plus flags the same as in the integer
directive. */
const char*
gnu_libc_printf_pointer_format (tree arg, const char **flags)
{
*flags = " +";
return arg && integer_zerop (arg) ? "(nil)" : "%#zx";
}

View File

@ -208,3 +208,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
# define TARGET_LIBC_HAS_FUNCTION linux_libc_has_function
#endif
/* The format string to which "%p" corresponds. */
#undef TARGET_PRINTF_POINTER_FORMAT
#define TARGET_PRINTF_POINTER_FORMAT gnu_libc_printf_pointer_format

View File

@ -30,6 +30,9 @@ along with GCC; see the file COPYING3. If not see
#include "varasm.h"
#include "output.h"
#undef TARGET_PRINTF_POINTER_FORMAT
#define TARGET_PRINTF_POINTER_FORMAT solaris_printf_pointer_format
tree solaris_pending_aligns, solaris_pending_inits, solaris_pending_finis;
/* Attach any pending attributes for DECL to the list in *ATTRIBUTES.
@ -297,3 +300,14 @@ solaris_override_options (void)
if (!HAVE_LD_EH_FRAME_CIEV3 && !global_options_set.x_dwarf_version)
dwarf_version = 2;
}
/* Solaris libc formats pointers as if by "%zx" with the pound ('#')
format flag having the same meaning as in the integer directive. */
const char*
solaris_printf_pointer_format (tree, const char **flags)
{
*flags = "#";
return "%zx";
}

View File

@ -440,6 +440,10 @@ along with GCC; see the file COPYING3. If not see
#undef TARGET_LIBC_HAS_FUNCTION
#define TARGET_LIBC_HAS_FUNCTION default_libc_has_function
/* The format string to which "%p" corresponds. */
#undef TARGET_LIBC_PRINTF_POINTER_FORMAT
#define TARGET_LIBC_PRINTF_POINTER_FORMAT solaris_libc_printf_pointer_format
extern GTY(()) tree solaris_pending_aligns;
extern GTY(()) tree solaris_pending_inits;
extern GTY(()) tree solaris_pending_finis;

View File

@ -268,7 +268,8 @@ Objective-C and Objective-C++ Dialects}.
-Wno-div-by-zero -Wdouble-promotion -Wduplicated-cond @gol
-Wempty-body -Wenum-compare -Wno-endif-labels @gol
-Werror -Werror=* -Wfatal-errors -Wfloat-equal -Wformat -Wformat=2 @gol
-Wno-format-contains-nul -Wno-format-extra-args -Wformat-nonliteral @gol
-Wno-format-contains-nul -Wno-format-extra-args -Wformat-length=@var{n} @gol
-Wformat-nonliteral @gol
-Wformat-security -Wformat-signedness -Wformat-y2k -Wframe-address @gol
-Wframe-larger-than=@var{len} -Wno-free-nonheap-object -Wjump-misses-init @gol
-Wignored-qualifiers -Wignored-attributes -Wincompatible-pointer-types @gol
@ -379,7 +380,7 @@ Objective-C and Objective-C++ Dialects}.
-fno-toplevel-reorder -fno-trapping-math -fno-zero-initialized-in-bss @gol
-fomit-frame-pointer -foptimize-sibling-calls @gol
-fpartial-inlining -fpeel-loops -fpredictive-commoning @gol
-fprefetch-loop-arrays @gol
-fprefetch-loop-arrays -fprintf-return-value @gol
-fprofile-correction @gol
-fprofile-use -fprofile-use=@var{path} -fprofile-values @gol
-fprofile-reorder-functions @gol
@ -3888,6 +3889,88 @@ in the case of @code{scanf} formats, this option suppresses the
warning if the unused arguments are all pointers, since the Single
Unix Specification says that such unused arguments are allowed.
@item -Wformat-length
@itemx -Wformat-length=@var{level}
@opindex Wformat-length
@opindex Wno-format-length
Warn about calls to formatted input/output functions such as @code{sprintf}
that might overflow the destination buffer, or about bounded functions such
as @code{snprintf} that might result in output truncation. When the exact
number of bytes written by a format directive cannot be determined at
compile-time it is estimated based on heuristics that depend on the
@var{level} argument and on optimization. While enabling optimization
will in most cases improve the accuracy of the warning, it may also
result in false positives.
@table @gcctabopt
@item -Wformat-length
@item -Wformat-length=1
@opindex Wformat-length
@opindex Wno-format-length
Level @var{1} of @option{-Wformat-length} enabled by @option{-Wformat}
employs a conservative approach that warns only about calls that most
likely overflow the buffer or result in output truncation. At this
level, numeric arguments to format directives with unknown values are
assumed to have the value of one, and strings of unknown length to be
empty. Numeric arguments that are known to be bounded to a subrange
of their type, or string arguments whose output is bounded either by
their directive's precision or by a finite set of string literals, are
assumed to take on the value within the range that results in the most
bytes on output. For example, the call to @code{sprintf} below is
diagnosed because even with both @var{a} and @var{b} equal to zero,
the terminating NUL character (@code{'\0'}) appended by the function
to the destination buffer will be written past its end. Increasing
the size of the buffer by a single byte is sufficient to avoid the
warning, though it may not be sufficient to avoid the overflow.
@smallexample
void f (int a, int b)
@{
char buf [12];
sprintf (buf, "a = %i, b = %i\n", a, b);
@}
@end smallexample
@item -Wformat-length=2
Level @var{2} warns also about calls that might overflow the destination
buffer or result in truncation given an argument of sufficient length
or magnitude. At level @var{2}, unknown numeric arguments are assumed
to have the minimum representable value for signed types with a precision
greater than 1, and the maximum representable value otherwise. Unknown
string arguments whose length cannot be assumed to be bounded either by
the directive's precision, or by a finite set of string literals they
may evaluate to, or the character array they may point to, are assumed
to be 1 character long.
At level @var{2}, the call in the example above is again diagnosed, but
this time because with @var{a} equal to a 32-bit @code{INT_MIN} the first
@code{%i} directive will write some of its digits beyond the end of
the destination buffer. To make the call safe regardless of the values
of the two variables, the size of the destination buffer must be increased
to at least 34 bytes. GCC includes the minimum size of the buffer in
an informational note following the warning.
An alternative to increasing the size of the destination buffer is to
constrain the range of formatted values. The maximum length of string
arguments can be bounded by specifying the precision in the format
directive. When numeric arguments of format directives can be assumed
to be bounded by less than the precision of their type, choosing
an appropriate length modifier to the format specifier will reduce
the required buffer size. For example, if @var{a} and @var{b} in the
example above can be assumed to be within the precision of
the @code{short int} type then using either the @code{%hi} format
directive or casting the argument to @code{short} reduces the maximum
required size of the buffer to 24 bytes.
@smallexample
void f (int a, int b)
@{
char buf [23];
sprintf (buf, "a = %hi, b = %i\n", a, (short)b);
@}
@end smallexample
@end table
@item -Wno-format-zero-length
@opindex Wno-format-zero-length
@opindex Wformat-zero-length
@ -7855,6 +7938,30 @@ dependent on the structure of loops within the source code.
Disabled at level @option{-Os}.
@item -fprintf-return-value
@opindex fprintf-return-value
Substitute constants for known return value of formatted output functions
such as @code{sprintf}, @code{snprintf}, @code{vsprintf}, and @code{vsnprintf}
(but not @code{printf} of @code{fprintf}). This transformation allows GCC
to optimize or even eliminate branches based on the known return value of
these functions called with arguments that are either constant, or whose
values are known to be in a range that makes determining the exact return
value possible. For example, both the branch and the body of the @code{if}
statement (but not the call to @code{snprint}) can be optimized away when
@code{i} is a 32-bit or smaller integer because the return value is guaranteed
to be at most 8.
@smallexample
char buf[9];
if (snprintf (buf, "%08x", i) >= sizeof buf)
@dots{}
@end smallexample
The @option{-fprintf-return-value} option relies on other optimizations
and yields best results with @option{-O2}. It works in tandem with the
@option{-Wformat-length} option. The @option{-fprintf-return-value}
option is disabled by default.
@item -fno-peephole
@itemx -fno-peephole2
@opindex fno-peephole

View File

@ -5332,6 +5332,10 @@ In either case, it remains possible to select code-generation for the alternate
scheme, by means of compiler command line switches.
@end defmac
@deftypefn {Target Hook} {const char*} TARGET_PRINTF_POINTER_FORMAT (tree, const char **@var{flags})
Determine the target @code{printf} implementation format string that the most closely corresponds to the @code{%p} format directive. The object pointed to by the @var{flags} is set to a string consisting of recognized format flags such as the @code{'#'} character.
@end deftypefn
@node Addressing Modes
@section Addressing Modes
@cindex addressing modes

View File

@ -4065,6 +4065,8 @@ In either case, it remains possible to select code-generation for the alternate
scheme, by means of compiler command line switches.
@end defmac
@hook TARGET_PRINTF_POINTER_FORMAT
@node Addressing Modes
@section Addressing Modes
@cindex addressing modes

View File

@ -1159,21 +1159,30 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
}
/* Return the string length, maximum string length or maximum value of
ARG in LENGTH.
If ARG is an SSA name variable, follow its use-def chains. If LENGTH
is not NULL and, for TYPE == 0, its value is not equal to the length
we determine or if we are unable to determine the length or value,
return false. VISITED is a bitmap of visited variables.
TYPE is 0 if string length should be returned, 1 for maximum string
length and 2 for maximum value ARG can have. */
/* Obtain the minimum and maximum string length or minimum and maximum
value of ARG in LENGTH[0] and LENGTH[1], respectively.
If ARG is an SSA name variable, follow its use-def chains. When
TYPE == 0, if LENGTH[1] is not equal to the length we determine or
if we are unable to determine the length or value, return False.
VISITED is a bitmap of visited variables.
TYPE is 0 if string length should be obtained, 1 for maximum string
length and 2 for maximum value ARG can have.
When FUZZY is set and the length of a string cannot be determined,
the function instead considers as the maximum possible length the
size of a character array it may refer to. */
static bool
get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
bool fuzzy)
{
tree var, val;
gimple *def_stmt;
/* The minimum and maximum length. The MAXLEN pointer stays unchanged
but MINLEN may be cleared during the execution of the function. */
tree *minlen = length;
tree *const maxlen = length + 1;
if (TREE_CODE (arg) != SSA_NAME)
{
/* We can end up with &(*iftmp_1)[0] here as well, so handle it. */
@ -1184,8 +1193,8 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
tree aop0 = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
if (TREE_CODE (aop0) == INDIRECT_REF
&& TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
return get_maxval_strlen (TREE_OPERAND (aop0, 0),
length, visited, type);
return get_range_strlen (TREE_OPERAND (aop0, 0),
length, visited, type, fuzzy);
}
if (type == 2)
@ -1197,26 +1206,60 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
}
else
val = c_strlen (arg, 1);
if (!val && fuzzy)
{
if (TREE_CODE (arg) == ADDR_EXPR)
return get_range_strlen (TREE_OPERAND (arg, 0), length,
visited, type, fuzzy);
if (TREE_CODE (arg) == COMPONENT_REF
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1))) == ARRAY_TYPE)
{
/* Use the type of the member array to determine the upper
bound on the length of the array. This may be overly
optimistic if the array itself isn't NUL-terminated and
the caller relies on the subsequent member to contain
the NUL. */
arg = TREE_OPERAND (arg, 1);
val = TYPE_SIZE_UNIT (TREE_TYPE (arg));
if (!val || integer_zerop (val))
return false;
val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
integer_one_node);
/* Avoid using the array size as the minimum. */
minlen = NULL;
}
}
if (!val)
return false;
if (*length)
if (minlen
&& (!*minlen
|| (type > 0
&& TREE_CODE (*minlen) == INTEGER_CST
&& TREE_CODE (val) == INTEGER_CST
&& tree_int_cst_lt (val, *minlen))))
*minlen = val;
if (*maxlen)
{
if (type > 0)
{
if (TREE_CODE (*length) != INTEGER_CST
if (TREE_CODE (*maxlen) != INTEGER_CST
|| TREE_CODE (val) != INTEGER_CST)
return false;
if (tree_int_cst_lt (*length, val))
*length = val;
if (tree_int_cst_lt (*maxlen, val))
*maxlen = val;
return true;
}
else if (simple_cst_equal (val, *length) != 1)
else if (simple_cst_equal (val, *maxlen) != 1)
return false;
}
*length = val;
*maxlen = val;
return true;
}
@ -1244,14 +1287,14 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
|| gimple_assign_unary_nop_p (def_stmt))
{
tree rhs = gimple_assign_rhs1 (def_stmt);
return get_maxval_strlen (rhs, length, visited, type);
return get_range_strlen (rhs, length, visited, type, fuzzy);
}
else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR)
{
tree op2 = gimple_assign_rhs2 (def_stmt);
tree op3 = gimple_assign_rhs3 (def_stmt);
return get_maxval_strlen (op2, length, visited, type)
&& get_maxval_strlen (op3, length, visited, type);
return get_range_strlen (op2, length, visited, type, fuzzy)
&& get_range_strlen (op3, length, visited, type, fuzzy);
}
return false;
@ -1274,8 +1317,13 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
if (arg == gimple_phi_result (def_stmt))
continue;
if (!get_maxval_strlen (arg, length, visited, type))
return false;
if (!get_range_strlen (arg, length, visited, type, fuzzy))
{
if (fuzzy)
*maxlen = build_all_ones_cst (size_type_node);
else
return false;
}
}
}
return true;
@ -1285,17 +1333,40 @@ get_maxval_strlen (tree arg, tree *length, bitmap *visited, int type)
}
}
/* Determine the minimum and maximum value or string length that ARG
refers to and store each in the first two elements of MINMAXLEN.
For expressions that point to strings of unknown lengths that are
character arrays, use the upper bound of the array as the maximum
length. For example, given an expression like 'x ? array : "xyz"'
and array declared as 'char array[8]', MINMAXLEN[0] will be set
to 3 and MINMAXLEN[1] to 7, the longest string that could be
stored in array.
*/
void get_range_strlen (tree arg, tree minmaxlen[2])
{
bitmap visited = NULL;
minmaxlen[0] = NULL_TREE;
minmaxlen[1] = NULL_TREE;
get_range_strlen (arg, minmaxlen, &visited, 1, true);
if (visited)
BITMAP_FREE (visited);
}
tree
get_maxval_strlen (tree arg, int type)
{
bitmap visited = NULL;
tree len = NULL_TREE;
if (!get_maxval_strlen (arg, &len, &visited, type))
len = NULL_TREE;
tree len[2] = { NULL_TREE, NULL_TREE };
if (!get_range_strlen (arg, len, &visited, type, false))
len[1] = NULL_TREE;
if (visited)
BITMAP_FREE (visited);
return len;
return len[1];
}

View File

@ -24,6 +24,8 @@ along with GCC; see the file COPYING3. If not see
extern tree canonicalize_constructor_val (tree, tree);
extern tree get_symbol_constant_value (tree);
extern void get_range_strlen (tree, tree[2]);
extern tree get_maxval_strlen (tree, int);
extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
extern bool fold_stmt (gimple_stmt_iterator *);
extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree));

2686
gcc/gimple-ssa-sprintf.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -43,6 +43,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_warn_function_return);
NEXT_PASS (pass_expand_omp);
NEXT_PASS (pass_build_cgraph_edges);
NEXT_PASS (pass_sprintf_length, false);
TERMINATE_PASS_LIST (all_lowering_passes)
/* Interprocedural optimization passes. */
@ -306,6 +307,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_simduid_cleanup);
NEXT_PASS (pass_lower_vector_ssa);
NEXT_PASS (pass_cse_reciprocals);
NEXT_PASS (pass_sprintf_length, true);
NEXT_PASS (pass_reassoc, false /* insert_powi_p */);
NEXT_PASS (pass_strength_reduction);
NEXT_PASS (pass_split_paths);

View File

@ -769,7 +769,8 @@ print_node (FILE *file, const char *prefix, tree node, int indent)
case VECTOR_CST:
{
char buf[10];
/* Big enough for 2 UINT_MAX plus the string below. */
char buf[32];
unsigned i;
for (i = 0; i < VECTOR_CST_NELTS (node); ++i)

View File

@ -3353,6 +3353,12 @@ greater than 128 and a multiple of 32.",
machine_mode, (int n, bool extended),
default_floatn_mode)
DEFHOOK
(printf_pointer_format,
"Determine the target @code{printf} implementation format string that the most closely corresponds to the @code{%p} format directive. The object pointed to by the @var{flags} is set to a string consisting of recognized format flags such as the @code{'#'} character.",
const char*, (tree, const char **flags),
default_printf_pointer_format)
/* Compute cost of moving data from a register of class FROM to one of
TO, using MODE. */
DEFHOOK

View File

@ -1509,6 +1509,20 @@ no_c99_libc_has_function (enum function_class fn_class ATTRIBUTE_UNUSED)
return false;
}
/* Return the format string to which "%p" corresponds. By default,
assume it corresponds to the C99 "%zx" format and set *FLAGS to
the empty string to indicate that format flags have no effect.
An example of an implementation that matches this description
is AIX. */
const char*
default_printf_pointer_format (tree, const char **flags)
{
*flags = "";
return "%zx";
}
tree
default_builtin_tm_load_store (tree ARG_UNUSED (type))
{

View File

@ -191,6 +191,10 @@ extern bool default_libc_has_function (enum function_class);
extern bool no_c99_libc_has_function (enum function_class);
extern bool gnu_libc_has_function (enum function_class);
extern const char* default_printf_pointer_format (tree, const char **);
extern const char* gnu_libc_printf_pointer_format (tree, const char **);
extern const char* solaris_printf_pointer_format (tree, const char **);
extern tree default_builtin_tm_load_store (tree);
extern int default_memory_move_cost (machine_mode, reg_class_t, bool);

View File

@ -1,3 +1,14 @@
2016-09-20 Martin Sebor <msebor@redhat.com>
PR middle-end/49905
* gcc.dg/builtin-stringop-chk-1.c: Adjust.
* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf-warn-2.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf-warn-4.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf-2.c: New test.
2016-09-21 Kugan Vivekanandarajah <kuganv@linaro.org>
* gcc.dg/guality/pr54519-1.c: Add -fno-ipa-vrp. Else constant

View File

@ -1,7 +1,7 @@
/* Test whether buffer overflow warnings for __*_chk builtins
are emitted properly. */
/* { dg-do compile } */
/* { dg-options "-O2 -std=gnu99 -ftrack-macro-expansion=0" } */
/* { dg-options "-O2 -Wno-format -std=gnu99 -ftrack-macro-expansion=0" } */
/* { dg-additional-options "-mstructure-size-boundary=8" { target arm*-*-* } } */
// { dg-skip-if "packed attribute missing for t" { "epiphany-*-*" } { "*" } { "" } }

View File

@ -0,0 +1,218 @@
/* Test to verify that the return value of calls to __builtin_sprintf
is not folded if the call has undefined behavior even if it would
otherwise produce a known number of bytes on output, and that if
the return value is in a known range the range is not made
available to subsequent passes and doesn't affect branching and
the removal of code.
The test is compiled with warnings disabled to make sure the absence
of optimizations does not depend on the presence of warnings. */
/* { dg-do compile } */
/* { dg-options "-O2 -fprintf-return-value -fdump-tree-optimized -ftrack-macro-expansion=0 -w" } */
#ifndef LINE
# define LINE 0
#endif
#define INT_MAX __INT_MAX__
char *buf;
char buf8k [8192];
#define concat(a, b) a ## b
#define CAT(a, b) concat (a, b)
#define EQL(expect, size, fmt, ...) \
void CAT (test_on_line_, __LINE__)(void) \
{ \
if (!LINE || LINE == __LINE__) \
{ \
char *dst = size < 0 ? buf : buf8k + sizeof buf8k - size; \
int result = __builtin_sprintf (dst, fmt, __VA_ARGS__); \
if (result != expect) \
__builtin_abort (); \
} \
}
/* Verify that the return value or range or return values from the call
to the formatted function is not treated as a constant or made available
to subsequent optimization passes. */
#define RNG(min, max, size, fmt, ...) \
void CAT (test_on_line_, __LINE__)(void) \
{ \
if (!LINE || LINE == __LINE__) \
{ \
char *dst = size < 0 ? buf : buf8k + sizeof buf8k - size; \
int result = __builtin_sprintf (dst, fmt, __VA_ARGS__); \
if (result < min || max < result) \
__builtin_abort (); \
} \
}
extern int i;
extern long li;
extern char *str;
/* Verify that overflowing the destination object disables the return
value optimization. */
EQL (0, 0, "%c", ' ');
EQL (0, 0, "%c", i)
EQL (0, 0, "%-s", "");
EQL (1, 1, "%c", 'x');
EQL (1, 1, "%-s", "x");
EQL (4, 4, "%4c", 'x');
/* Verify that exceeding the environmental limit of 4095 bytes for
a single conversion specification disables the return value
folding. */
EQL ( 4096, sizeof buf8k, "%4096c", 'x');
EQL (INT_MAX, -1, "%*c", INT_MAX, 'x');
EQL ( 4096, sizeof buf8k, "%4096.4094f", 1.0);
EQL ( 4096, sizeof buf8k, "%.4094f", 1.0);
EQL ( 4097, sizeof buf8k, "%.4095f", 1.0);
enum { imax2 = (INT_MAX / 2) * 2 };
EQL (imax2, -1, "%*c%*c", INT_MAX / 2, 'x', INT_MAX / 2, 'y');
/* Verify that range inforation for calls that overflow the destination
isn't available. */
RNG (0, 0, 0, "%hhi", i)
RNG (0, 0, 1, "%hhi", i)
RNG (0, 1, 1, "%hhi", i)
RNG (0, 0, 2, "%hhi", i)
RNG (0, 1, 2, "%hhi", i)
RNG (0, 2, 2, "%hhi", i)
RNG (0, 0, 3, "%hhi", i)
RNG (0, 1, 3, "%hhi", i)
RNG (0, 2, 3, "%hhi", i)
RNG (0, 3, 3, "%hhi", i)
RNG (0, 0, 4, "%hhi", i)
RNG (0, 1, 4, "%hhi", i)
RNG (0, 2, 4, "%hhi", i)
RNG (0, 3, 4, "%hhi", i)
RNG (0, 4, 4, "%hhi", i)
RNG (0, 0, 0, "%hhu", i)
RNG (0, 0, 1, "%hhu", i)
RNG (0, 1, 1, "%hhu", i)
RNG (0, 0, 2, "%hhu", i)
RNG (0, 1, 2, "%hhu", i)
RNG (0, 2, 2, "%hhu", i)
RNG (0, 0, 3, "%hhu", i)
RNG (0, 1, 3, "%hhu", i)
RNG (0, 2, 3, "%hhu", i)
RNG (0, 3, 3, "%hhu", i)
RNG (0, 0, 0, "%i", i)
RNG (0, 0, 1, "%i", i)
RNG (0, 1, 1, "%i", i)
RNG (0, 0, 2, "%i", i)
RNG (0, 1, 2, "%i", i)
RNG (0, 2, 2, "%i", i)
RNG (0, 0, 3, "%i", i)
RNG (0, 1, 3, "%i", i)
RNG (0, 2, 3, "%i", i)
RNG (0, 3, 3, "%i", i)
RNG (0, 0, 4, "%i", i)
RNG (0, 1, 4, "%i", i)
RNG (0, 2, 4, "%i", i)
RNG (0, 3, 4, "%i", i)
RNG (0, 4, 4, "%i", i)
RNG (0, 0, 5, "%i", i)
RNG (0, 1, 5, "%i", i)
RNG (0, 2, 5, "%i", i)
RNG (0, 3, 5, "%i", i)
RNG (0, 4, 5, "%i", i)
RNG (0, 5, 5, "%i", i)
RNG (0, 0, 6, "%i", i)
RNG (0, 1, 6, "%i", i)
RNG (0, 2, 6, "%i", i)
RNG (0, 3, 6, "%i", i)
RNG (0, 4, 6, "%i", i)
RNG (0, 5, 6, "%i", i)
RNG (0, 6, 6, "%i", i)
RNG (0, 0, 7, "%i", i)
RNG (0, 1, 7, "%i", i)
RNG (0, 2, 7, "%i", i)
RNG (0, 3, 7, "%i", i)
RNG (0, 4, 7, "%i", i)
RNG (0, 5, 7, "%i", i)
RNG (0, 6, 7, "%i", i)
#if __SIZEOF_INT__ == 4
/* A 32-bit int takes up at most 11 bytes (-2147483648) not including
the terminating nul. */
RNG (0, 7, 7, "%i", i)
RNG (0, 0, 8, "%i", i)
RNG (0, 1, 8, "%i", i)
RNG (0, 2, 8, "%i", i)
RNG (0, 3, 8, "%i", i)
RNG (0, 4, 8, "%i", i)
RNG (0, 5, 8, "%i", i)
RNG (0, 6, 8, "%i", i)
RNG (0, 7, 8, "%i", i)
RNG (0, 8, 8, "%i", i)
RNG (0, 0, 9, "%i", i)
RNG (0, 1, 9, "%i", i)
RNG (0, 2, 9, "%i", i)
RNG (0, 3, 9, "%i", i)
RNG (0, 4, 9, "%i", i)
RNG (0, 5, 9, "%i", i)
RNG (0, 6, 9, "%i", i)
RNG (0, 7, 9, "%i", i)
RNG (0, 8, 9, "%i", i)
RNG (0, 9, 9, "%i", i)
RNG (0, 0, 10, "%i", i)
RNG (0, 1, 10, "%i", i)
RNG (0, 2, 10, "%i", i)
RNG (0, 3, 10, "%i", i)
RNG (0, 4, 10, "%i", i)
RNG (0, 5, 10, "%i", i)
RNG (0, 6, 10, "%i", i)
RNG (0, 7, 10, "%i", i)
RNG (0, 8, 10, "%i", i)
RNG (0, 9, 10, "%i", i)
RNG (0, 10, 10, "%i", i)
#endif
/* Verify the result of a conditional expression involving a string
literal and an unknown string isn't optimized. */
RNG (0, 1, 4, "%-s", i ? str : "123");
RNG (0, 1, 4, "%-s", i ? "123" : str);
/* Verify that no call to abort has been eliminated and that each call
is at the beginning of a basic block (and thus the result of a branch).
This latter test tries to verify that the test preceding the call to
abort has not been eliminated either.
The expected output looks something like this:
<bb 2>:
result_3 = __builtin_sprintf (&MEM[(void *)&buf8k + 8192B], "%c", 32);
if (result_3 != 0)
goto <bb 3>;
else
goto <bb 4>;
<bb 3>:
__builtin_abort ();
*/
/* { dg-final { scan-tree-dump-times ">:\n *__builtin_abort" 105 "optimized" { target { ilp32 || lp64 } } } } */
/* { dg-final { scan-tree-dump-times ">:\n *__builtin_abort" 74 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } } */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,214 @@
/* { dg-do compile } */
/* { dg-options "-std=c99 -Wformat -Wformat-length=2 -ftrack-macro-expansion=0" } */
/* 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
char buffer [256];
extern char *ptr;
#define buffer(size) \
(!LINE || __LINE__ == LINE ? buffer + sizeof buffer - size : ptr)
#define objsize(size) (!LINE || __LINE__ == LINE ? size : __SIZE_MAX__ / 2)
typedef __SIZE_TYPE__ size_t;
#if !__cplusplus
typedef __WCHAR_TYPE__ wchar_t;
#endif
typedef unsigned char UChar;
#define T(size, fmt, ...) \
__builtin_sprintf (buffer (size), fmt, __VA_ARGS__)
__builtin_va_list va;
/* Exercise buffer overflow detection with const string arguments. */
void test_s_const (void)
{
/* Wide string literals are handled slightly differently than
at level 1. At level 1, each wide character is assumed to
convert into a single byte. At level 2, they are assumed
to convert into at least one byte. */
T (0, "%ls", L""); /* { dg-warning "nul past the end" } */
T (1, "%ls", L"");
T (1, "%ls", L"\0");
T (1, "%1ls", L""); /* { dg-warning "nul past the end" } */
T (0, "%*ls", 0, L""); /* { dg-warning "nul past the end" } */
T (1, "%*ls", 0, L"");
T (1, "%*ls", 0, L"\0");
T (1, "%*ls", 1, L""); /* { dg-warning "nul past the end" } */
T (1, "%ls", L"1"); /* { dg-warning "nul past the end" } */
T (1, "%.0ls", L"1");
T (2, "%.0ls", L"1");
T (2, "%.1ls", L"1");
/* The "%.2ls" directive below will write at a minimum 1 byte (because
L"1" is known and can be assumed to convert to at least one multibyte
character), and at most 2 bytes because of the precision. Since its
output is explicitly bounded it is diagnosed. */
T (2, "%.2ls", L"1"); /* { dg-warning "nul past the end" } */
T (2, "%.*ls", 2, L"1"); /* { dg-warning "nul past the end" } */
/* The following three are constrained by the precision to at most
that many bytes of the converted wide string plus a terminating NUL. */
T (2, "%.0ls", L"1");
T (2, "%.1ls", L"1");
T (3, "%.2ls", L"1");
}
struct Arrays {
char a1 [1];
char a2 [2];
char a3 [3];
char a4 [4];
char a0 [0];
char ax [];
};
/* Exercise buffer overflow detection with non-const string arguments. */
void test_s_nonconst (const char *s, const wchar_t *ws, struct Arrays *a)
{
T (0, "%s", s); /* { dg-warning "into a region" "sprintf transformed into strcpy" { xfail *-*-*-* } } */
T (1, "%s", s); /* { dg-warning "nul past the end" "sprintf transformed into strcpy" { xfail *-*-*-* } } */
T (1, "%1s", s); /* { dg-warning "nul past the end" } */
T (1, "%.0s", s);
T (1, "%.1s", s); /* { dg-warning "writing a terminating nul" } */
T (1, "%ls", ws); /* { dg-warning "writing a terminating nul" } */
/* Verify that the size of the array is used in lieu of its length.
The minus sign disables GCC's sprintf to strcpy transformation. */
T (1, "%-s", a->a1); /* { dg-warning "nul past the end" } */
/* In the following test, since the length of the strings isn't known,
their type (the array) is used to bound the maximum length to 1,
which means the "%-s" directive would not overflow the buffer,
but it would leave no room for the terminating nul. */
T (1, "%-s", a->a2); /* { dg-warning "writing a terminating nul" } */
/* Unlike in the test above, since the length of the string is bounded
by the array type to at most 2, the "^-s" directive is diagnosed firts,
preventing the diagnostic about the terminatinb nul. */
T (1, "%-s", a->a3); /* { dg-warning "directive writing between 1 and 2 bytes" } */
/* The length of a zero length array and flexible array member is
unknown and at leve 2 assumed to be at least 1. */
T (1, "%-s", a->a0); /* { dg-warning "nul past the end" } */
T (1, "%-s", a->ax); /* { dg-warning "nul past the end" } */
T (2, "%-s", a->a0);
T (2, "%-s", a->ax);
}
/* Exercise buffer overflow detection with non-const integer arguments. */
void test_hh_nonconst (int x)
{
T (1, "%hhi", x); /* { dg-warning "into a region" } */
T (2, "%hhi", x); /* { dg-warning "into a region" } */
T (3, "%hhi", x); /* { dg-warning "into a region" } */
T (4, "%hhi", x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
}
void test_h_nonconst (int x)
{
extern UChar uc;
T (1, "%hi", uc); /* { dg-warning "into a region" } */
T (2, "%hi", uc); /* { dg-warning "into a region" } */
/* Formatting an 8-bit unsigned char as a signed short (or any other
type with greater precision) can write at most 3 characters. */
T (3, "%hi", uc); /* { dg-warning "terminating nul past" } */
T (4, "%hi", uc);
/* Verify that the same thing works when the int argument is cast
to unsigned char. */
T (1, "%hi", (UChar)x); /* { dg-warning "into a region" } */
T (2, "%hi", (UChar)x); /* { dg-warning "into a region" } */
T (3, "%hi", (UChar)x); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T (4, "%hi", (UChar)x);
}
void test_i_nonconst (int x)
{
extern UChar uc;
T (1, "%i", uc); /* { dg-warning "into a region" } */
T (2, "%i", uc); /* { dg-warning "into a region" } */
T (3, "%i", uc); /* { dg-warning "terminating nul past" } */
T (4, "%i", uc);
T (1, "%i", (UChar)x); /* { dg-warning "into a region" } */
T (2, "%i", (UChar)x); /* { dg-warning "into a region" } */
T (3, "%i", (UChar)x); /* { dg-warning "terminating nul past" } */
T (4, "%i", (UChar)x);
/* Verify the same thing using a bit-field. */
extern struct {
unsigned int b1: 1;
unsigned int b2: 2;
unsigned int b3: 3;
unsigned int b4: 4;
int sb4: 4;
unsigned int b5: 5;
unsigned int b6: 6;
unsigned int b7: 7;
unsigned int b8: 8;
} bf, abf[], *pbf;
T (1, "%i", bf.b1); /* { dg-warning "nul past the end" } */
T (1, "%i", abf [x].b1); /* { dg-warning "nul past the end" } */
T (1, "%i", pbf->b1); /* { dg-warning "nul past the end" } */
/* A one bit bit-field can only be formatted as '0' or '1'. Similarly,
two- and three-bit bit-fields can only be formatted as a single
decimal digit. */
T (2, "%i", bf.b1);
T (2, "%i", abf [x].b1);
T (2, "%i", pbf->b1);
T (2, "%i", bf.b2);
T (2, "%i", abf [x].b2);
T (2, "%i", pbf->b2);
T (2, "%i", bf.b3);
T (2, "%i", abf [x].b3);
T (2, "%i", pbf->b3);
/* A four-bit bit-field can be formatted as either one or two digits. */
T (2, "%i", bf.b4); /* { dg-warning "nul past the end" } */
T (2, "%i", abf [x].b4); /* { dg-warning "nul past the end" } */
T (2, "%i", pbf->b4); /* { dg-warning "nul past the end" } */
T (3, "%i", bf.b4);
T (3, "%i", pbf->b4);
T (3, "%i", bf.b5);
T (3, "%i", pbf->b5);
T (3, "%i", bf.b6);
T (3, "%i", pbf->b6);
T (3, "%i", bf.b7); /* { dg-warning "nul past the end" } */
T (3, "%i", pbf->b7); /* { dg-warning "nul past the end" } */
T (1, "%i", bf.b8); /* { dg-warning "into a region" } */
T (2, "%i", bf.b8); /* { dg-warning "into a region" } */
/* Formatting an 8-bit unsigned char as a signed short (or any other
type with greater precision) int can write at most 3 characters. */
T (3, "%i", bf.b8); /* { dg-warning "terminating nul past" } */
T (4, "%i", bf.b8);
T (1, "%i", bf.b8); /* { dg-warning "into a region" } */
T (2, "%i", bf.b8); /* { dg-warning "into a region" } */
T (3, "%i", bf.b8); /* { dg-warning "terminating nul past" } */
T (2, "%i", bf.sb4); /* { dg-warning "terminating nul past" } */
T (3, "%i", bf.sb4);
T (4, "%i", bf.sb4);
}

View File

@ -0,0 +1,234 @@
/* { dg-do compile } */
/* { dg-options "-std=c99 -O2 -Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */
#ifndef LINE
# define LINE 0
#endif
#define bos(x) __builtin_object_size (x, 0)
#define T(bufsize, fmt, ...) \
do { \
if (!LINE || __LINE__ == LINE) \
{ \
char *d = (char *)__builtin_malloc (bufsize); \
__builtin___sprintf_chk (d, 0, bos (d), fmt, __VA_ARGS__); \
sink (d); \
} \
} while (0)
void
sink (void*);
/* Identity function to verify that the checker figures out the value
of the operand even when it's not constant (i.e., makes use of
inlining and constant propagation information). */
int i (int x) { return x; }
const char* s (const char *str) { return str; }
/* Function to "generate" a unique unknown number (as far as GCC can
tell) each time it's called. It prevents the optimizer from being
able to narrow down the ranges of possible values in test functions
with repeated references to the same variable. */
extern int x (void);
/* Verify that the checker can detect buffer overflow when the "%s"
argument is in a known range of lengths and one or both of which
exceed the size of the destination. */
void test_sprintf_chk_string (const char *s, const char *t)
{
#define x x ()
T (1, "%s", x ? "" : "1"); /* { dg-warning "nul past the end" } */
T (1, "%s", x ? "1" : ""); /* { dg-warning "nul past the end" } */
T (1, "%s", x ? s : "1"); /* { dg-warning "nul past the end" } */
T (1, "%s", x ? "1" : s); /* { dg-warning "nul past the end" } */
T (1, "%s", x ? s : t);
T (2, "%s", x ? "" : "1");
T (2, "%s", x ? "" : s);
T (2, "%s", x ? "1" : "");
T (2, "%s", x ? s : "");
T (2, "%s", x ? "1" : "2");
T (2, "%s", x ? "" : "12"); /* { dg-warning "nul past the end" } */
T (2, "%s", x ? "12" : ""); /* { dg-warning "nul past the end" } */
T (2, "%s", x ? "" : "123"); /* { dg-warning "into a region" } */
T (2, "%s", x ? "123" : ""); /* { dg-warning "into a region" } */
#undef x
}
/* Verify that the checker makes use of integer constant propagation
to detect buffer overflow in non-constant cases. */
void test_sprintf_chk_integer_value (void)
{
T ( 1, "%i", i ( 0)); /* { dg-warning "nul past the end" } */
T ( 1, "%i", i ( 1)); /* { dg-warning "nul past the end" } */
T ( 1, "%i", i ( -1)); /* { dg-warning "into a region" } */
T ( 1, "%i_", i ( 1)); /* { dg-warning "character ._. at offset 2 past the end" } */
T ( 1, "_%i", i ( 1)); /* { dg-warning "into a region" } */
T ( 1, "_%i_",i ( 1)); /* { dg-warning "into a region" } */
T ( 1, "%o", i ( 0)); /* { dg-warning "nul past the end" } */
T ( 1, "%u", i ( 0)); /* { dg-warning "nul past the end" } */
T ( 1, "%x", i ( 0)); /* { dg-warning "nul past the end" } */
T ( 1, "%#x", i ( 0)); /* { dg-warning "nul past the end" } */
T ( 1, "%x", i ( 1)); /* { dg-warning "nul past the end" } */
T ( 1, "%#x", i ( 1)); /* { dg-warning "into a region" } */
T ( 2, "%i", i ( 0));
T ( 2, "%i", i ( 1));
T ( 2, "%i", i ( 9));
T ( 2, "%i", i ( -1)); /* { dg-warning "nul past the end" } */
T ( 2, "%i", i ( 10)); /* { dg-warning "nul past the end" } */
T ( 2, "%i_", i ( 0)); /* { dg-warning "nul past the end" } */
T ( 2, "_%i", i ( 0)); /* { dg-warning "nul past the end" } */
T ( 2, "_%i_",i ( 0)); /* { dg-warning "character ._. at offset 3 past the end" } */
T ( 2, "%o", i ( 1));
T ( 2, "%o", i ( 7));
T ( 2, "%o", i ( 010)); /* { dg-warning "nul past the end" } */
T ( 2, "%o", i ( 0100)); /* { dg-warning "into a region" } */
T ( 2, "%x", i ( 1));
T ( 2, "%#x", i ( 1)); /* { dg-warning "into a region" } */
T ( 2, "%x", i ( 0xa));
T ( 2, "%x", i ( 0xf));
T ( 2, "%x", i ( 0x10)); /* { dg-warning "nul past the end" } */
T ( 2, "%x", i ( 0xff)); /* { dg-warning "nul past the end" } */
T ( 2, "%x", i (0x1ff)); /* { dg-warning "into a region" } */
T ( 3, "%i", i ( 0));
T ( 3, "%i", i ( 1));
T ( 3, "%i", i ( 9));
T ( 3, "%i", i ( -9));
T ( 3, "%i", i ( 10));
T ( 3, "%i", i ( 99));
T ( 3, "%i", i ( -99)); /* { dg-warning "nul past the end" } */
T ( 3, "%i", i (99) + i (1)); /* { dg-warning "nul past the end" } */
T ( 8, "%8u", i ( 1)); /* { dg-warning "nul past the end" } */
T ( 9, "%8u", i ( 1));
}
/* Functions to require optimization to figure out the range of the operand.
Used to verify that the checker makes use of the range information to
avoid diagnosing the output of sufficiently constrained arguments to
integer directives. */
signed char*
range_schar (signed char *val, signed char min, signed char max)
{
if (*val < min || max < *val) __builtin_abort ();
return val;
}
unsigned char*
range_uchar (unsigned char *val, unsigned char min, unsigned char max)
{
if (*val < min || max < *val) __builtin_abort ();
return val;
}
signed short*
range_sshort (signed short *val, signed short min, signed short max)
{
if (*val < min || max < *val) __builtin_abort ();
return val;
}
unsigned short*
range_ushort (unsigned short *val, unsigned short min, unsigned short max)
{
if (*val < min || max < *val) __builtin_abort ();
return val;
}
/* Helper to prevent GCC from figuring out the return value. */
extern int idx (void);
/* Exercise ranges only in types signed and unsigned char and short.
No other types work due to bug 71690. */
void test_sprintf_chk_range_schar (signed char *a)
{
(void)&a;
/* Ra creates a range of signed char for A [idx]. A different
value is used each time to prevent the ranges from intesecting
one another, possibly even eliminating some tests as a result
of the range being empty. */
#define R(min, max) *range_schar (a + idx (), min, max)
T ( 0, "%i", R (0, 9)); /* { dg-warning ".%i. directive writing 1 byte into a region of size 0" } */
T ( 1, "%i", R (0, 9)); /* { dg-warning "nul past the end" } */
T ( 2, "%i", R (0, 9));
T ( 2, "%i", R (-1, 0)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T ( 2, "%i", R (9, 10)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T ( 3, "%i", R ( -9, 9));
T ( 3, "%i", R (-99, 99)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T ( 3, "%i", R ( 0, 99));
T ( 3, "%i", R ( 0, 100)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
/* The following call may write as few as 3 bytes and as many as 5.
It's judgment call how best to diagnose it to make the potential
problem clear. */
T ( 3, "%i%i", R (1, 10), R (9, 10)); /* { dg-warning ".%i. directive writing between 1 and 2 bytes into a region of size 1" } */
T ( 4, "%i%i", R (10, 11), R (12, 13)); /* { dg-warning "nul past the end" } */
T ( 5, "%i%i", R (-9, 99), R (-9, 99));
T ( 6, "%i_%i_%i", R (0, 9), R (0, 9), R (0, 9));
T ( 6, "%i_%i_%i", R (0, 9), R (0, 9), R (0, 10)); /* { dg-warning "may write a terminating nul past the end" } */
T ( 6, "%i_%i_%i", R (0, 9), R (0, 10), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */
T ( 6, "%i_%i_%i", R (0, 10), R (0, 9), R (0, 9)); /* { dg-warning "may write a terminating nul past the end" } */
T ( 6, "%i_%i_%i", R (0, 9), R (0, 10), R (0, 10)); /* { dg-warning ".%i. directive writing between 1 and 2 bytes into a region of size 1" } */
}
void test_sprintf_chk_range_uchar (unsigned char *a, unsigned char *b)
{
(void)&a;
(void)&b;
#undef Ra
#define Ra(min, max) *range_uchar (a + idx (), min, max)
T ( 0, "%i", Ra (0, 9)); /* { dg-warning ".%i. directive writing 1 byte into a region of size 0" } */
T ( 1, "%i", Ra (0, 9)); /* { dg-warning "nul past the end" } */
T ( 2, "%i", Ra (0, 9));
T ( 2, "%i", Ra (9, 10)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T ( 3, "%i", Ra (0, 99));
T ( 3, "%i", Ra (0, 100)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
}
void test_sprintf_chk_range_sshort (signed short *a, signed short *b)
{
(void)&a;
(void)&b;
#undef Ra
#define Ra(min, max) *range_sshort (a + idx (), min, max)
T ( 0, "%i", Ra ( 0, 9)); /* { dg-warning ".%i. directive writing 1 byte into a region of size 0" } */
T ( 1, "%i", Ra ( 0, 1)); /* { dg-warning "nul past the end" } */
T ( 1, "%i", Ra ( 0, 9)); /* { dg-warning "nul past the end" } */
T ( 2, "%i", Ra ( 0, 1));
T ( 2, "%i", Ra ( 8, 9));
T ( 2, "%i", Ra ( 0, 9));
T ( 2, "%i", Ra (-1, 0)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T ( 2, "%i", Ra ( 9, 10)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T ( 3, "%i", Ra ( 0, 99));
T ( 3, "%i", Ra (99, 999)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
T ( 4, "%i", Ra ( 0, 999));
T ( 4, "%i", Ra ( 99, 999));
T ( 4, "%i", Ra (998, 999));
T ( 4, "%i", Ra (999, 1000)); /* { dg-warning "may write a terminating nul past the end of the destination" } */
}

View File

@ -0,0 +1,33 @@
/* { dg-do compile } */
/* { dg-options "-Wformat -Wformat-length=1 -fdiagnostics-show-caret -ftrack-macro-expansion=0" } */
extern int sprintf (char*, const char*, ...);
char dst [8];
void test (void)
{
sprintf (dst + 7, "%-s", "1");
/* { dg-warning "writing a terminating nul past the end of the destination" "" { target *-*-*-* } 10 }
{ dg-message "format output 2 bytes into a destination of size 1" "" { target *-*-*-* } 10 }
{ dg-begin-multiline-output "" }
sprintf (dst + 7, "%-s", "1");
^~~~~
{ dg-end-multiline-output "" }
{ dg-begin-multiline-output "" }
sprintf (dst + 7, "%-s", "1");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */
sprintf (dst + 7, "%-s", "abcd");
/* { dg-warning ".%-s. directive writing 4 bytes into a region of size 1" "" { target *-*-*-* } 22 }
{ dg-message "format output 5 bytes into a destination of size 1" "" { target *-*-*-* } 22 }
{ dg-begin-multiline-output "" }
sprintf (dst + 7, "%-s", "abcd");
^~~ ~~~~~~
{ dg-end-multiline-output "" }
{ dg-begin-multiline-output "" }
sprintf (dst + 7, "%-s", "abcd");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */
}

View File

@ -0,0 +1,540 @@
/* Test to verify that the return value of calls to __builtin_sprintf
that produce a known number of bytes on output is available for
constant folding. With optimization enabled the test will fail to
link if any of the assertions fails. Without optimization the test
aborts at runtime if any of the assertions fails. */
/* { dg-do run } */
/* { dg-additional-options "-O2 -Wall -Wno-pedantic -fprintf-return-value" } */
#ifndef LINE
# define LINE 0
#endif
#if __STDC_VERSION__ < 199901L
# define __func__ __FUNCTION__
#endif
typedef __SIZE_TYPE__ size_t;
unsigned ntests;
unsigned nfails;
void __attribute__ ((noclone, noinline))
checkv (const char *func, int line, int res, int min, int max,
char *dst, const char *fmt, __builtin_va_list va)
{
int n = __builtin_vsprintf (dst, fmt, va);
int len = __builtin_strlen (dst);
++ntests;
int fail = 0;
if (n != res)
{
__builtin_printf ("FAIL: %s:%i: \"%s\" expected result for \"%s\" "
"doesn't match function call return value: ",
func, line, fmt, dst);
if (min == max)
__builtin_printf ("%i != %i\n", n, min);
else
__builtin_printf ("%i not in [%i, %i]\n", n, min, max);
fail = 1;
}
else
{
if (len < min || max < len)
{
__builtin_printf ("FAIL: %s:%i: \"%s\" expected result for \"%s\" "
"doesn't match output length: ",
func, line, fmt, dst);
if (min == max)
__builtin_printf ("%i != %i\n", len, min);
else
__builtin_printf ("%i not in [%i, %i]\n", len, min, max);
fail = 1;
}
else
__builtin_printf ("PASS: %s:%i: \"%s\" result %i: \"%s\"\n",
func, line, fmt, n, dst);
}
if (fail)
++nfails;
}
void __attribute__ ((noclone, noinline))
check (const char *func, int line, int res, int min, int max,
char *dst, const char *fmt, ...)
{
__builtin_va_list va;
__builtin_va_start (va, fmt);
checkv (func, line, res, min, max, dst, fmt, va);
__builtin_va_end (va);
}
char buffer[256];
char* volatile dst = buffer;
char* ptr = buffer;
#define concat(a, b) a ## b
#define CAT(a, b) concat (a, b)
#if __OPTIMIZE__
/* With optimization references to the following undefined symbol which
is unique for each test case are expected to be eliminated. */
# define TEST_FAILURE(line, ignore1, ignore2, ignore3) \
do { \
extern void CAT (failure_on_line_, line)(void); \
CAT (failure_on_line_, line)(); \
} while (0)
#else
/* The test is run by DejaGnu with optimization enabled. When it's run
with it disabled (i.e., at -O0) each test case is verified at runtime
and the test aborts just before exiting if any of them failed. */
# define TEST_FAILURE(line, result, min, max) \
if (min == max) \
__builtin_printf ("FAIL: %s:%i: expected %i, got %i\n", \
__func__, line, min, result); \
else \
__builtin_printf ("FAIL: %s:%i: expected range [%i, %i], got %i\n", \
__func__, line, min, max, result);
#endif
/* Verify that the result is exactly equal to RES. */
#define EQL(expect, size, fmt, ...) \
if (!LINE || LINE == __LINE__) \
do { \
char *buf = (size) < 0 ? ptr : buffer + sizeof buffer - (size); \
int result = __builtin_sprintf (buf, fmt, __VA_ARGS__); \
if (result != expect) \
{ \
TEST_FAILURE (__LINE__, expect, expect, result); \
} \
check (__func__, __LINE__, result, expect, expect, dst, fmt, \
__VA_ARGS__); \
} while (0)
/* Verify that the result is in the range [MIN, MAX]. */
#define RNG(min, max, size, fmt, ...) \
if (!LINE || LINE == __LINE__) \
do { \
char *buf = (size) < 0 ? ptr : buffer + sizeof buffer - (size); \
int result = __builtin_sprintf (buf, fmt, __VA_ARGS__); \
if (result < min || max < result) \
{ \
TEST_FAILURE (__LINE__, min, max, result); \
} \
check (__func__, __LINE__, result, min, max, dst, fmt, \
__VA_ARGS__); \
} while (0)
static void __attribute__ ((noinline, noclone))
test_c (char c)
{
EQL (1, 2, "%c", c);
EQL (1, -1, "%c", c);
EQL (1, 2, "%1c", c);
EQL (1, -1, "%1c", c);
EQL (1, 2, "%*c", 1, c);
EQL (1, -1, "%*c", 1, c);
EQL (2, 3, "%c%c", '1', '2');
EQL (2, -1, "%c%c", '1', '2');
EQL (3, 4, "%3c", c);
EQL (3, -1, "%3c", c);
EQL (3, 4, "%*c", 3, c);
EQL (3, -1, "%*c", 3, c);
EQL (3, 4, "%*c%*c", 2, c, 1, c);
EQL (3, 4, "%*c%*c", 1, c, 2, c);
EQL (3, 4, "%c%c%c", '1', '2', '3');
EQL (3, 4, "%*c%c%c", 1, '1', '2', '3');
EQL (3, 4, "%*c%*c%c", 1, '1', 1, '2', '3');
EQL (3, 4, "%*c%*c%*c", 1, '1', 1, '2', 1, '3');
EQL (3, -1, "%*c%*c", 2, c, 1, c);
EQL (3, -1, "%*c%*c", 1, c, 2, c);
EQL (3, -1, "%c%c%c", '1', '2', '3');
EQL (3, -1, "%*c%c%c", 1, '1', '2', '3');
EQL (3, -1, "%*c%*c%c", 1, '1', 1, '2', '3');
EQL (3, -1, "%*c%*c%*c", 1, '1', 1, '2', 1, '3');
EQL (4, 5, "%c%c %c", '1', '2', '3');
EQL (5, 6, "%c %c %c", '1', '2', '3');
EQL (5, 6, "%c %c %c", c, c, c);
}
/* Generate a pseudo-random value in the specified range. The return
value must be unsigned char to work around limitations in the GCC
range information. Similarly for the declaration of rand() whose
correct return value should be int, but that also prevents the range
information from making it to the printf pass. */
unsigned char uchar_range (unsigned min, unsigned max)
{
extern unsigned rand (void);
unsigned x;
x = rand ();
if (x < min)
x = min;
else if (max < x)
x = max;
return x;
}
static void __attribute__ ((noinline, noclone))
test_d_i (int i, long li)
{
/* +-------------------------- expected return value */
/* | +---------------------- destination size */
/* | | +------------------- format string */
/* | | | +-- variable argument(s) */
/* | | | | */
/* V V V V */
EQL ( 1, 2, "%d", 0);
EQL ( 2, 3, "%d%d", 0, 1);
EQL ( 3, 4, "%d%d", 9, 10);
EQL ( 4, 5, "%d%d", 11, 12);
EQL ( 5, 6, "%d:%d", 12, 34);
EQL ( 5, 6, "%d", 12345);
EQL ( 6, 7, "%d", -12345);
EQL (15, 16, "%d:%d:%d:%d", 123, 124, 125, 126);
EQL ( 1, 2, "%i", uchar_range (0, 9));
EQL ( 1, -1, "%i", uchar_range (0, 9));
/* The range information available to passes other than the Value
Range Propoagation pass itself is so bad that the following two
tests fail (the range seen in the test below is [0, 99] rather
than [10, 99].
EQL ( 2, 3, "%i", uchar_range (10, 99));
EQL ( 3, 4, "%i", uchar_range (100, 199));
*/
/* Verify that the width allows the return value in the following
calls can be folded despite the unknown value of the argument. */
#if __SIZEOF_INT__ == 2
EQL ( 6, 7, "%6d", i);
EQL ( 6, 7, "%+6d", i);
EQL ( 6, 7, "%-6d", i);
EQL ( 6, 7, "%06d", i);
#elif __SIZEOF_INT__ == 4
EQL (11, 12, "%11d", i);
EQL (11, 12, "%+11d", i);
EQL (11, 12, "%-11d", i);
EQL (11, 12, "%011d", i);
#elif __SIZEOF_INT__ == 8
EQL (20, 21, "%20d", i);
EQL (20, 21, "%+20d", i);
EQL (20, 21, "%-29d", i);
EQL (20, 21, "%020d", i);
#endif
#if __SIZEOF_LONG__ == 2
EQL ( 6, 7, "%6ld", li);
EQL ( 6, 7, "%+6ld", li);
EQL ( 6, 7, "%-6ld", li);
EQL ( 6, 7, "%06ld", li);
#elif __SIZEOF_LONG__ == 4
EQL (11, 12, "%11ld", li);
EQL (11, 12, "%+11ld", li);
EQL (11, 12, "%-11ld", li);
EQL (11, 12, "%011ld", li);
#elif __SIZEOF_LONG__ == 8
EQL (20, 21, "%20ld", li);
EQL (20, 21, "%+20ld", li);
EQL (20, 21, "%-20ld", li);
EQL (20, 21, "%020ld", li);
#endif
/* Verify that the output of a directive with an unknown argument
is correctly determined at compile time to be in the expected
range. */
/* +---------------------------- expected minimum return value */
/* | +------------------------ expected maximum return value */
/* | | +-------------------- destination size */
/* | | | +----------------- format string */
/* | | | | +----- variable argument(s) */
/* | | | | | */
/* V V V V V */
RNG ( 1, 4, 5, "%hhi", i);
RNG ( 1, 3, 4, "%hhu", i);
#if __SIZEOF_SHORT__ == 2
RNG ( 1, 6, 7, "%hi", i);
RNG ( 1, 5, 6, "%hu", i);
#elif __SIZEOF_SHORT__ == 4
RNG ( 1, 11, 12, "%hi", i);
RNG ( 1, 10, 11, "%hu", i);
#endif
#if __SIZEOF_INT__ == 2
RNG ( 1, 6, 7, "%i", i);
RNG ( 1, 5, 6, "%u", i);
#elif __SIZEOF_INT__ == 4
RNG ( 1, 11, 12, "%i", i);
RNG ( 1, 10, 11, "%u", i);
#elif __SIZEOF_INT__ == 8
RNG ( 1, 20, 21, "%i", i);
RNG ( 1, 19, 20, "%u", i);
#endif
#if __SIZEOF_LONG__ == 4
RNG ( 1, 11, 12, "%li", li);
RNG ( 1, 10, 11, "%lu", li);
#elif __SIZEOF_LONG__ == 8
RNG ( 1, 20, 21, "%li", li);
RNG ( 1, 19, 20, "%lu", li);
#endif
}
static void __attribute__ ((noinline, noclone))
test_x (unsigned char uc, unsigned short us, unsigned ui)
{
EQL ( 1, 2, "%hhx", 0);
EQL ( 2, 3, "%2hhx", 0);
EQL ( 2, 3, "%02hhx", 0);
EQL ( 2, 3, "%#02hhx", 0);
EQL ( 1, 2, "%hhx", 1);
EQL ( 2, 3, "%2hhx", 1);
EQL ( 2, 3, "%02hhx", 1);
EQL ( 3, 4, "%#02hhx", 1);
EQL ( 2, 3, "%2hhx", uc);
EQL ( 2, 3, "%02hhx", uc);
EQL ( 5, 6, "%#05hhx", uc);
EQL ( 2, 3, "%2hhx", us);
EQL ( 2, 3, "%02hhx", us);
EQL ( 5, 6, "%#05hhx", us);
EQL ( 2, 3, "%2hhx", ui);
EQL ( 2, 3, "%02hhx", ui);
EQL ( 5, 6, "%#05hhx", ui);
EQL ( 1, 2, "%x", 0);
EQL ( 1, 2, "%#x", 0);
EQL ( 1, 2, "%#0x", 0);
EQL ( 1, 2, "%x", 1);
EQL ( 1, 2, "%x", 0xf);
EQL ( 2, 3, "%x", 0x10);
EQL ( 2, 3, "%x", 0xff);
EQL ( 3, 4, "%x", 0x100);
EQL (11, 12, "%02x:%02x:%02x:%02x", 0xde, 0xad, 0xbe, 0xef);
/* The following would be optimized if the range information of
the variable's type was made available. Alas, it's lost due
to the promotion of the actual argument (unsined char) to
the type of the "formal" argument (int in the case of the
ellipsis).
EQL (11, 12, "%02x:%02x:%02x:%02x", uc, uc, uc, uc);
*/
EQL (11, 12, "%02hhx:%02hhx:%02hhx:%02hhx", uc, uc, uc, uc);
#if __SIZEOF_SHORT__ == 2
EQL ( 4, 5, "%04hx", us);
EQL ( 9, 10, "%04hx:%04hx", us, us);
EQL (14, 15, "%04hx:%04hx:%04hx", us, us, us);
EQL (19, 20, "%04hx:%04hx:%04hx:%04hx", us, us, us, us);
#endif
#if __SIZEOF_INT__ == 2
EQL ( 4, 5, "%04x", ui);
EQL ( 6, 7, "%#06x", ui);
#elif __SIZEOF_INT__ == 4
EQL ( 8, 9, "%08x", ui);
EQL (10, 10 + 1, "%#010x", ui);
#elif __SIZEOF_INT__ == 8
EQL (16, 17, "%016x", ui);
EQL (18, 19, "%#018x", ui);
#endif
}
static void __attribute__ ((noinline, noclone))
test_a_double (void)
{
EQL ( 6, 7, "%a", 0.0); /* 0x0p+0 */
EQL ( 6, 7, "%a", 1.0); /* 0x8p-3 */
EQL ( 6, 7, "%a", 2.0); /* 0x8p-2 */
EQL ( 8, 9, "%.1a", 3.0); /* 0xc.0p-2 */
EQL ( 9, 10, "%.2a", 4.0); /* 0xa.00p-1 */
}
static void __attribute__ ((noinline, noclone))
test_a_long_double (void)
{
EQL ( 6, 7, "%La", 0.0L); /* 0x0p+0 */
EQL ( 6, 7, "%La", 1.0L); /* 0x8p-3 */
EQL ( 6, 7, "%La", 2.0L); /* 0x8p-2 */
EQL ( 8, 9, "%.1La", 3.0L); /* 0xc.0p-2 */
EQL ( 9, 10, "%.2La", 4.0L); /* 0xa.00p-1 */
}
static void __attribute__ ((noinline, noclone))
test_e_double (void)
{
EQL (12, 13, "%e", 1.0e0);
EQL (13, 14, "%e", -1.0e0);
EQL (12, 13, "%e", 1.0e+1);
EQL (13, 14, "%e", -1.0e+1);
EQL (12, 13, "%e", 1.0e+12);
EQL (13, 14, "%e", -1.0e+12);
EQL (13, 14, "%e", 1.0e+123);
EQL (14, 15, "%e", -1.0e+123);
EQL (12, 13, "%e", 9.999e+99);
EQL (12, 13, "%e", 9.9999e+99);
EQL (12, 13, "%e", 9.99999e+99);
/* The actual output of the following directive depends on the rounding
mode. */
/* EQL (12, "%e", 9.9999994e+99); */
EQL (12, 13, "%e", 1.0e-1);
EQL (12, 13, "%e", 1.0e-12);
EQL (13, 14, "%e", 1.0e-123);
}
static void __attribute__ ((noinline, noclone))
test_e_long_double (void)
{
EQL (12, 13, "%Le", 1.0e0L);
EQL (13, 14, "%Le", -1.0e0L);
EQL (12, 13, "%Le", 1.0e+1L);
EQL (13, 14, "%Le", -1.0e+1L);
EQL (12, 13, "%Le", 1.0e+12L);
EQL (13, 14, "%Le", -1.0e+12L);
EQL (13, 14, "%Le", 1.0e+123L);
EQL (14, 15, "%Le", -1.0e+123L);
EQL (12, 13, "%Le", 9.999e+99L);
EQL (12, 13, "%Le", 9.9999e+99L);
EQL (12, 13, "%Le", 9.99999e+99L);
EQL (12, 13, "%Le", 9.999999e+99L);
/* The actual output of the following directive depends on the rounding
mode. */
/* EQL (12, "%Le", 9.9999994e+99L); */
EQL (12, 13, "%Le", 1.0e-1L);
EQL (12, 13, "%Le", 1.0e-12L);
EQL (13, 14, "%Le", 1.0e-123L);
EQL ( 6, 7, "%.0Le", 1.0e-111L);
EQL ( 8, 9, "%.1Le", 1.0e-111L);
EQL (19, 20, "%.12Le", 1.0e-112L);
EQL (20, 21, "%.13Le", 1.0e-113L);
}
static void __attribute__ ((noinline, noclone))
test_f_double (void)
{
EQL ( 8, 9, "%f", 0.0e0);
EQL ( 8, 9, "%f", 0.1e0);
EQL ( 8, 9, "%f", 0.12e0);
EQL ( 8, 9, "%f", 0.123e0);
EQL ( 8, 9, "%f", 0.1234e0);
EQL ( 8, 9, "%f", 0.12345e0);
EQL ( 8, 9, "%f", 0.123456e0);
EQL ( 8, 9, "%f", 1.234567e0);
EQL ( 9, 10, "%f", 1.0e+1);
EQL ( 20, 21, "%f", 1.0e+12);
EQL (130, 131, "%f", 1.0e+123);
EQL ( 8, 9, "%f", 1.0e-1);
EQL ( 8, 9, "%f", 1.0e-12);
EQL ( 8, 9, "%f", 1.0e-123);
}
static void __attribute__ ((noinline, noclone))
test_f_long_double (void)
{
EQL ( 8, 9, "%Lf", 0.0e0L);
EQL ( 8, 9, "%Lf", 0.1e0L);
EQL ( 8, 9, "%Lf", 0.12e0L);
EQL ( 8, 9, "%Lf", 0.123e0L);
EQL ( 8, 9, "%Lf", 0.1234e0L);
EQL ( 8, 9, "%Lf", 0.12345e0L);
EQL ( 8, 9, "%Lf", 0.123456e0L);
EQL ( 8, 9, "%Lf", 1.234567e0L);
EQL ( 9, 10, "%Lf", 1.0e+1L);
EQL ( 20, 21, "%Lf", 1.0e+12L);
EQL (130, 131, "%Lf", 1.0e+123L);
EQL ( 8, 9, "%Lf", 1.0e-1L);
EQL ( 8, 9, "%Lf", 1.0e-12L);
EQL ( 8, 9, "%Lf", 1.0e-123L);
}
static void __attribute__ ((noinline, noclone))
test_s (int i)
{
EQL ( 0, 1, "%s", "");
EQL ( 0, 1, "%s", "\0");
EQL ( 1, 2, "%1s", "");
EQL ( 1, 2, "%s", "1");
EQL ( 2, 3, "%2s", "");
EQL ( 2, 3, "%s", "12");
EQL ( 2, 3, "%s%s", "12", "");
EQL ( 2, 3, "%s%s", "", "12");
EQL ( 2, 3, "%s%s", "1", "2");
EQL ( 3, 4, "%3s", "");
EQL ( 3, 4, "%3s", "1");
EQL ( 3, 4, "%3s", "12");
EQL ( 3, 4, "%3s", "123");
EQL ( 3, 4, "%3.3s", "1");
EQL ( 3, 4, "%3.3s", "12");
EQL ( 3, 4, "%3.3s", "123");
EQL ( 3, 4, "%3.3s", "1234");
EQL ( 3, 4, "%3.3s", "12345");
EQL ( 3, 4, "%s %s", "1", "2");
EQL ( 4, 5, "%s %s", "12", "3");
EQL ( 5, 6, "%s %s", "12", "34");
EQL ( 5, 6, "[%s %s]", "1", "2");
EQL ( 6, 7, "[%s %s]", "12", "3");
EQL ( 7, 8, "[%s %s]", "12", "34");
/* Verify the result of a conditional expression involving string
literals is in the expected range of their lengths. */
RNG ( 0, 3, 4, "%-s", i ? "" : "123");
RNG ( 1, 4, 5, "%-s", i ? "1" : "1234");
RNG ( 2, 5, 6, "%-s", i ? "12" : "12345");
RNG ( 3, 6, 7, "%-s", i ? "123" : "123456");
}
int main (void)
{
test_c ('?');
test_d_i (0xdeadbeef, 0xdeadbeefL);
test_x ('?', 0xdead, 0xdeadbeef);
test_a_double ();
test_e_double ();
test_f_double ();
test_a_long_double ();
test_e_long_double ();
test_f_long_double ();
test_s (0);
if (nfails)
{
__builtin_printf ("%u out of %u tests failed\n", nfails, ntests);
__builtin_abort ();
}
return 0;
}

View File

@ -471,6 +471,7 @@ extern simple_ipa_opt_pass *make_pass_ipa_oacc (gcc::context *ctxt);
extern simple_ipa_opt_pass *make_pass_ipa_oacc_kernels (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_gen_hsail (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_warn_nonnull_compare (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_sprintf_length (gcc::context *ctxt);
/* IPA Passes */
extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt);