Commit 22fca489 by Martin Sebor Committed by Martin Sebor

PR tree-optimization/83431 - -Wformat-truncation may incorrectly report truncation

gcc/ChangeLog:

	PR c++/83431
	* gimple-ssa-sprintf.c (pass_data_sprintf_length): Remove object.
	(sprintf_dom_walker): Remove class.
	(get_int_range): Make argument const.
	(directive::fmtfunc, directive::set_precision): Same.
	(format_none): Same.
	(build_intmax_type_nodes): Same.
	(adjust_range_for_overflow): Same.
	(format_floating): Same.
	(format_character): Same.
	(format_string): Same.
	(format_plain): Same.
	(get_int_range): Cast away constness.
	(format_integer): Same.
	(get_string_length): Call get_range_strlen_dynamic.  Handle
	null lendata.maxbound.
	(should_warn_p): Adjust argument scope qualifier.
	(maybe_warn): Same.
	(format_directive): Same.
	(parse_directive): Same.
	(is_call_safe): Same.
	(try_substitute_return_value): Same.
	(sprintf_dom_walker::handle_printf_call): Rename...
	(handle_printf_call): ...to this.  Initialize target to host charmap
	here instead of in pass_sprintf_length::execute.
	(struct call_info): Make global.
	(sprintf_dom_walker::compute_format_length): Make global.
	(sprintf_dom_walker::handle_gimple_call): Same.
	* passes.def (pass_sprintf_length): Replace with pass_strlen.
	* print-rtl.c (print_pattern): Reduce the number of spaces to
	avoid -Wformat-truncation.
	* tree-pass.h (make_pass_warn_printf): New function.
	* tree-ssa-strlen.c (strlen_optimize): New variable.
	(get_string_length): Add comments.
	(get_range_strlen_dynamic): New function.
	(check_and_optimize_call): New function.
	(handle_integral_assign): New function.
	(strlen_check_and_optimize_stmt): Factor code out into
	strlen_check_and_optimize_call and handle_integral_assign.
	(strlen_dom_walker::evrp): New member.
	(strlen_dom_walker::before_dom_children): Use evrp member.
	(strlen_dom_walker::after_dom_children): Use evrp member.
	(printf_strlen_execute): New function.
	(pass_strlen::gate): Update to handle printf calls.
	(dump_strlen_info): New function.
	(pass_data_warn_printf): New variable.
	(pass_warn_printf): New class.
	* tree-ssa-strlen.h (get_range_strlen_dynamic): Declare.
	(handle_printf_call): Same.
	* tree-vrp.c (value_range_base::type): Adjust assertion.
	* vr-values.c (vr_values::update_value_range): Use type of the first
	argument rather than the second.

gcc/testsuite/ChangeLog:

	PR c++/83431
	* gcc.dg/strlenopt-63.c: New test.
	* gcc.dg/pr79538.c: Adjust text of expected warning.
	* gcc.dg/pr81292-1.c: Adjust pass name.
	* gcc.dg/pr81292-2.c: Same.
	* gcc.dg/pr81703.c: Same.
	* gcc.dg/strcmpopt_2.c: Same.
	* gcc.dg/strcmpopt_3.c: Same.
	* gcc.dg/strcmpopt_4.c: Same.
	* gcc.dg/strlenopt-1.c: Same.
	* gcc.dg/strlenopt-10.c: Same.
	* gcc.dg/strlenopt-11.c: Same.
	* gcc.dg/strlenopt-13.c: Same.
	* gcc.dg/strlenopt-14g.c: Same.
	* gcc.dg/strlenopt-14gf.c: Same.
	* gcc.dg/strlenopt-15.c: Same.
	* gcc.dg/strlenopt-16g.c: Same.
	* gcc.dg/strlenopt-17g.c: Same.
	* gcc.dg/strlenopt-18g.c: Same.
	* gcc.dg/strlenopt-19.c: Same.
	* gcc.dg/strlenopt-1f.c: Same.
	* gcc.dg/strlenopt-2.c: Same.
	* gcc.dg/strlenopt-20.c: Same.
	* gcc.dg/strlenopt-21.c: Same.
	* gcc.dg/strlenopt-22.c: Same.
	* gcc.dg/strlenopt-22g.c: Same.
	* gcc.dg/strlenopt-24.c: Same.
	* gcc.dg/strlenopt-25.c: Same.
	* gcc.dg/strlenopt-26.c: Same.
	* gcc.dg/strlenopt-27.c: Same.
	* gcc.dg/strlenopt-28.c: Same.
	* gcc.dg/strlenopt-29.c: Same.
	* gcc.dg/strlenopt-2f.c: Same.
	* gcc.dg/strlenopt-3.c: Same.
	* gcc.dg/strlenopt-30.c: Same.
	* gcc.dg/strlenopt-31g.c: Same.
	* gcc.dg/strlenopt-32.c: Same.
	* gcc.dg/strlenopt-33.c: Same.
	* gcc.dg/strlenopt-33g.c: Same.
	* gcc.dg/strlenopt-34.c: Same.
	* gcc.dg/strlenopt-35.c: Same.
	* gcc.dg/strlenopt-4.c: Same.
	* gcc.dg/strlenopt-48.c: Same.
	* gcc.dg/strlenopt-49.c: Same.
	* gcc.dg/strlenopt-4g.c: Same.
	* gcc.dg/strlenopt-4gf.c: Same.
	* gcc.dg/strlenopt-5.c: Same.
	* gcc.dg/strlenopt-50.c: Same.
	* gcc.dg/strlenopt-51.c: Same.
	* gcc.dg/strlenopt-52.c: Same.
	* gcc.dg/strlenopt-53.c: Same.
	* gcc.dg/strlenopt-54.c: Same.
	* gcc.dg/strlenopt-55.c: Same.
	* gcc.dg/strlenopt-56.c: Same.
	* gcc.dg/strlenopt-6.c: Same.
	* gcc.dg/strlenopt-61.c: Same.
	* gcc.dg/strlenopt-7.c: Same.
	* gcc.dg/strlenopt-8.c: Same.
	* gcc.dg/strlenopt-9.c: Same.
	* gcc.dg/strlenopt.h (snprintf, snprintf): Declare.
	* gcc.dg/tree-ssa/builtin-snprintf-6.c: New test.
	* gcc.dg/tree-ssa/builtin-snprintf-7.c: New test.
	* gcc.dg/tree-ssa/builtin-snprintf-8.c: New test.
	* gcc.dg/tree-ssa/builtin-snprintf-9.c: New test.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-21.c: New test.
	* gcc.dg/tree-ssa/dump-4.c: New test.
	* gcc.dg/tree-ssa/pr83501.c: Adjust pass name.

From-SVN: r274933
parent 59bce4ad
2019-08-23 Martin Sebor <msebor@redhat.com>
PR c++/83431
* gimple-ssa-sprintf.c (pass_data_sprintf_length): Remove object.
(sprintf_dom_walker): Remove class.
(get_int_range): Make argument const.
(directive::fmtfunc, directive::set_precision): Same.
(format_none): Same.
(build_intmax_type_nodes): Same.
(adjust_range_for_overflow): Same.
(format_floating): Same.
(format_character): Same.
(format_string): Same.
(format_plain): Same.
(get_int_range): Cast away constness.
(format_integer): Same.
(get_string_length): Call get_range_strlen_dynamic. Handle
null lendata.maxbound.
(should_warn_p): Adjust argument scope qualifier.
(maybe_warn): Same.
(format_directive): Same.
(parse_directive): Same.
(is_call_safe): Same.
(try_substitute_return_value): Same.
(sprintf_dom_walker::handle_printf_call): Rename...
(handle_printf_call): ...to this. Initialize target to host charmap
here instead of in pass_sprintf_length::execute.
(struct call_info): Make global.
(sprintf_dom_walker::compute_format_length): Make global.
(sprintf_dom_walker::handle_gimple_call): Same.
* passes.def (pass_sprintf_length): Replace with pass_strlen.
* print-rtl.c (print_pattern): Reduce the number of spaces to
avoid -Wformat-truncation.
* tree-pass.h (make_pass_warn_printf): New function.
* tree-ssa-strlen.c (strlen_optimize): New variable.
(get_string_length): Add comments.
(get_range_strlen_dynamic): New function.
(check_and_optimize_call): New function.
(handle_integral_assign): New function.
(strlen_check_and_optimize_stmt): Factor code out into
strlen_check_and_optimize_call and handle_integral_assign.
(strlen_dom_walker::evrp): New member.
(strlen_dom_walker::before_dom_children): Use evrp member.
(strlen_dom_walker::after_dom_children): Use evrp member.
(printf_strlen_execute): New function.
(pass_strlen::gate): Update to handle printf calls.
(dump_strlen_info): New function.
(pass_data_warn_printf): New variable.
(pass_warn_printf): New class.
* tree-ssa-strlen.h (get_range_strlen_dynamic): Declare.
(handle_printf_call): Same.
* tree-vrp.c (value_range_base::type): Adjust assertion.
* vr-values.c (vr_values::update_value_range): Use type of the first
argument rather than the second.
2019-08-26 Richard Biener <rguenther@suse.de> 2019-08-26 Richard Biener <rguenther@suse.de>
* config/i386/i386-features.c (general_remove_non_convertible_regs): * config/i386/i386-features.c (general_remove_non_convertible_regs):
......
...@@ -85,7 +85,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -85,7 +85,7 @@ along with GCC; see the file COPYING3. If not see
#include "domwalk.h" #include "domwalk.h"
#include "alloc-pool.h" #include "alloc-pool.h"
#include "vr-values.h" #include "vr-values.h"
#include "gimple-ssa-evrp-analyze.h" #include "tree-ssa-strlen.h"
/* The likely worst case value of MB_LEN_MAX for the target, large enough /* The likely worst case value of MB_LEN_MAX for the target, large enough
for UTF-8. Ideally, this would be obtained by a target hook if it were for UTF-8. Ideally, this would be obtained by a target hook if it were
...@@ -100,80 +100,15 @@ along with GCC; see the file COPYING3. If not see ...@@ -100,80 +100,15 @@ along with GCC; see the file COPYING3. If not see
namespace { namespace {
const pass_data pass_data_sprintf_length = {
GIMPLE_PASS, // pass type
"printf-return-value", // pass name
OPTGROUP_NONE, // optinfo_flags
TV_NONE, // tv_id
PROP_cfg, // properties_required
0, // properties_provided
0, // properties_destroyed
0, // properties_start
0, // properties_finish
};
/* Set to the warning level for the current function which is equal /* Set to the warning level for the current function which is equal
either to warn_format_trunc for bounded functions or to either to warn_format_trunc for bounded functions or to
warn_format_overflow otherwise. */ warn_format_overflow otherwise. */
static int warn_level; static int warn_level;
struct call_info;
struct format_result; struct format_result;
class sprintf_dom_walker : public dom_walker
{
public:
sprintf_dom_walker ()
: dom_walker (CDI_DOMINATORS),
evrp_range_analyzer (false) {}
~sprintf_dom_walker () {}
edge before_dom_children (basic_block) FINAL OVERRIDE;
void after_dom_children (basic_block) FINAL OVERRIDE;
bool handle_gimple_call (gimple_stmt_iterator *);
struct call_info;
bool compute_format_length (call_info &, format_result *);
class evrp_range_analyzer evrp_range_analyzer;
};
class pass_sprintf_length : public gimple_opt_pass
{
bool fold_return_value;
public:
pass_sprintf_length (gcc::context *ctxt)
: gimple_opt_pass (pass_data_sprintf_length, ctxt),
fold_return_value (false)
{ }
opt_pass * clone () { return new pass_sprintf_length (m_ctxt); }
virtual bool gate (function *);
virtual unsigned int execute (function *);
void set_pass_param (unsigned int n, bool param)
{
gcc_assert (n == 0);
fold_return_value = param;
}
};
bool
pass_sprintf_length::gate (function *)
{
/* Run the pass iff -Warn-format-overflow or -Warn-format-truncation
is specified and either not optimizing and the pass is being invoked
early, or when optimizing and the pass is being invoked during
optimization (i.e., "late"). */
return ((warn_format_overflow > 0
|| warn_format_trunc > 0
|| flag_printf_return_value)
&& (optimize > 0) == fold_return_value);
}
/* The minimum, maximum, likely, and unlikely maximum number of bytes /* The minimum, maximum, likely, and unlikely maximum number of bytes
of output either a formatting function or an individual directive of output either a formatting function or an individual directive
can result in. */ can result in. */
...@@ -684,7 +619,7 @@ fmtresult::type_max_digits (tree type, int base) ...@@ -684,7 +619,7 @@ fmtresult::type_max_digits (tree type, int base)
static bool static bool
get_int_range (tree, HOST_WIDE_INT *, HOST_WIDE_INT *, bool, HOST_WIDE_INT, get_int_range (tree, HOST_WIDE_INT *, HOST_WIDE_INT *, bool, HOST_WIDE_INT,
class vr_values *vr_values); const vr_values *);
/* Description of a format directive. A directive is either a plain /* Description of a format directive. A directive is either a plain
string or a conversion specification that starts with '%'. */ string or a conversion specification that starts with '%'. */
...@@ -719,7 +654,7 @@ struct directive ...@@ -719,7 +654,7 @@ struct directive
/* Format conversion function that given a directive and an argument /* Format conversion function that given a directive and an argument
returns the formatting result. */ returns the formatting result. */
fmtresult (*fmtfunc) (const directive &, tree, vr_values *); fmtresult (*fmtfunc) (const directive &, tree, const vr_values *);
/* Return True when a the format flag CHR has been used. */ /* Return True when a the format flag CHR has been used. */
bool get_flag (char chr) const bool get_flag (char chr) const
...@@ -756,9 +691,9 @@ struct directive ...@@ -756,9 +691,9 @@ struct directive
or 0, whichever is greater. For a non-constant ARG in some range or 0, whichever is greater. For a non-constant ARG in some range
set width to its range adjusting each bound to -1 if it's less. set width to its range adjusting each bound to -1 if it's less.
For an indeterminate ARG set width to [0, INT_MAX]. */ For an indeterminate ARG set width to [0, INT_MAX]. */
void set_width (tree arg, vr_values *vr_values) void set_width (tree arg, const vr_values *vr)
{ {
get_int_range (arg, width, width + 1, true, 0, vr_values); get_int_range (arg, width, width + 1, true, 0, vr);
} }
/* Set both bounds of the precision range to VAL. */ /* Set both bounds of the precision range to VAL. */
...@@ -772,9 +707,9 @@ struct directive ...@@ -772,9 +707,9 @@ struct directive
or -1 whichever is greater. For a non-constant ARG in some range or -1 whichever is greater. For a non-constant ARG in some range
set precision to its range adjusting each bound to -1 if it's less. set precision to its range adjusting each bound to -1 if it's less.
For an indeterminate ARG set precision to [-1, INT_MAX]. */ For an indeterminate ARG set precision to [-1, INT_MAX]. */
void set_precision (tree arg, vr_values *vr_values) void set_precision (tree arg, const vr_values *vr)
{ {
get_int_range (arg, prec, prec + 1, false, -1, vr_values); get_int_range (arg, prec, prec + 1, false, -1, vr);
} }
/* Return true if both width and precision are known to be /* Return true if both width and precision are known to be
...@@ -904,7 +839,7 @@ bytes_remaining (unsigned HOST_WIDE_INT navail, const format_result &res) ...@@ -904,7 +839,7 @@ bytes_remaining (unsigned HOST_WIDE_INT navail, const format_result &res)
/* Description of a call to a formatted function. */ /* Description of a call to a formatted function. */
struct sprintf_dom_walker::call_info struct call_info
{ {
/* Function call statement. */ /* Function call statement. */
gimple *callstmt; gimple *callstmt;
...@@ -978,7 +913,7 @@ struct sprintf_dom_walker::call_info ...@@ -978,7 +913,7 @@ struct sprintf_dom_walker::call_info
/* Return the result of formatting a no-op directive (such as '%n'). */ /* Return the result of formatting a no-op directive (such as '%n'). */
static fmtresult static fmtresult
format_none (const directive &, tree, vr_values *) format_none (const directive &, tree, const vr_values *)
{ {
fmtresult res (0); fmtresult res (0);
return res; return res;
...@@ -987,7 +922,7 @@ format_none (const directive &, tree, vr_values *) ...@@ -987,7 +922,7 @@ format_none (const directive &, tree, vr_values *)
/* Return the result of formatting the '%%' directive. */ /* Return the result of formatting the '%%' directive. */
static fmtresult static fmtresult
format_percent (const directive &, tree, vr_values *) format_percent (const directive &, tree, const vr_values *)
{ {
fmtresult res (1); fmtresult res (1);
return res; return res;
...@@ -1047,7 +982,7 @@ build_intmax_type_nodes (tree *pintmax, tree *puintmax) ...@@ -1047,7 +982,7 @@ build_intmax_type_nodes (tree *pintmax, tree *puintmax)
static bool static bool
get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax, get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax,
bool absolute, HOST_WIDE_INT negbound, bool absolute, HOST_WIDE_INT negbound,
class vr_values *vr_values) const class vr_values *vr_values)
{ {
/* The type of the result. */ /* The type of the result. */
const_tree type = integer_type_node; const_tree type = integer_type_node;
...@@ -1086,7 +1021,9 @@ get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax, ...@@ -1086,7 +1021,9 @@ get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax,
&& TYPE_PRECISION (argtype) <= TYPE_PRECISION (type)) && TYPE_PRECISION (argtype) <= TYPE_PRECISION (type))
{ {
/* Try to determine the range of values of the integer argument. */ /* Try to determine the range of values of the integer argument. */
const value_range *vr = vr_values->get_value_range (arg); const value_range *vr
= CONST_CAST (class vr_values *, vr_values)->get_value_range (arg);
if (range_int_cst_p (vr)) if (range_int_cst_p (vr))
{ {
HOST_WIDE_INT type_min HOST_WIDE_INT type_min
...@@ -1203,7 +1140,7 @@ adjust_range_for_overflow (tree dirtype, tree *argmin, tree *argmax) ...@@ -1203,7 +1140,7 @@ adjust_range_for_overflow (tree dirtype, tree *argmin, tree *argmax)
used when the directive argument or its value isn't known. */ used when the directive argument or its value isn't known. */
static fmtresult static fmtresult
format_integer (const directive &dir, tree arg, vr_values *vr_values) format_integer (const directive &dir, tree arg, const vr_values *vr_values)
{ {
tree intmax_type_node; tree intmax_type_node;
tree uintmax_type_node; tree uintmax_type_node;
...@@ -1386,7 +1323,9 @@ format_integer (const directive &dir, tree arg, vr_values *vr_values) ...@@ -1386,7 +1323,9 @@ format_integer (const directive &dir, tree arg, vr_values *vr_values)
{ {
/* Try to determine the range of values of the integer argument /* Try to determine the range of values of the integer argument
(range information is not available for pointers). */ (range information is not available for pointers). */
const value_range *vr = vr_values->get_value_range (arg); const value_range *vr
= CONST_CAST (class vr_values *, vr_values)->get_value_range (arg);
if (range_int_cst_p (vr)) if (range_int_cst_p (vr))
{ {
argmin = vr->min (); argmin = vr->min ();
...@@ -1836,7 +1775,7 @@ format_floating (const directive &dir, const HOST_WIDE_INT prec[2]) ...@@ -1836,7 +1775,7 @@ format_floating (const directive &dir, const HOST_WIDE_INT prec[2])
ARG. */ ARG. */
static fmtresult static fmtresult
format_floating (const directive &dir, tree arg, vr_values *) format_floating (const directive &dir, tree arg, const vr_values *)
{ {
HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] }; HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] };
tree type = (dir.modifier == FMT_LEN_L || dir.modifier == FMT_LEN_ll tree type = (dir.modifier == FMT_LEN_L || dir.modifier == FMT_LEN_ll
...@@ -2030,21 +1969,33 @@ format_floating (const directive &dir, tree arg, vr_values *) ...@@ -2030,21 +1969,33 @@ format_floating (const directive &dir, tree arg, vr_values *)
Used by the format_string function below. */ Used by the format_string function below. */
static fmtresult static fmtresult
get_string_length (tree str, unsigned eltsize) get_string_length (tree str, unsigned eltsize, const vr_values *vr)
{ {
if (!str) if (!str)
return fmtresult (); return fmtresult ();
/* Determine the length of the shortest and longest string referenced /* Try to determine the dynamic string length first. */
by STR. Strings of unknown lengths are bounded by the sizes of
arrays that subexpressions of STR may refer to. Pointers that
aren't known to point any such arrays result in LENDATA.MAXLEN
set to SIZE_MAX. */
c_strlen_data lendata = { }; c_strlen_data lendata = { };
get_range_strlen (str, &lendata, eltsize); if (eltsize == 1)
get_range_strlen_dynamic (str, &lendata, vr);
else
{
/* Determine the length of the shortest and longest string referenced
by STR. Strings of unknown lengths are bounded by the sizes of
arrays that subexpressions of STR may refer to. Pointers that
aren't known to point any such arrays result in LENDATA.MAXLEN
set to SIZE_MAX. */
get_range_strlen (str, &lendata, eltsize);
}
/* LENDATA.MAXBOUND is null when LENDATA.MIN corresponds to the shortest
string referenced by STR. Otherwise, if it's not equal to .MINLEN it
corresponds to the bound of the largest array STR refers to, if known,
or it's SIZE_MAX otherwise. */
/* Return the default result when nothing is known about the string. */ /* Return the default result when nothing is known about the string. */
if (integer_all_onesp (lendata.maxbound) if (lendata.maxbound
&& integer_all_onesp (lendata.maxbound)
&& integer_all_onesp (lendata.maxlen)) && integer_all_onesp (lendata.maxlen))
return fmtresult (); return fmtresult ();
...@@ -2054,7 +2005,7 @@ get_string_length (tree str, unsigned eltsize) ...@@ -2054,7 +2005,7 @@ get_string_length (tree str, unsigned eltsize)
: 0); : 0);
HOST_WIDE_INT max HOST_WIDE_INT max
= (tree_fits_uhwi_p (lendata.maxbound) = (lendata.maxbound && tree_fits_uhwi_p (lendata.maxbound)
? tree_to_uhwi (lendata.maxbound) ? tree_to_uhwi (lendata.maxbound)
: HOST_WIDE_INT_M1U); : HOST_WIDE_INT_M1U);
...@@ -2093,10 +2044,11 @@ get_string_length (tree str, unsigned eltsize) ...@@ -2093,10 +2044,11 @@ get_string_length (tree str, unsigned eltsize)
else else
{ {
/* When the upper bound is unknown (it can be zero or excessive) /* When the upper bound is unknown (it can be zero or excessive)
set the likely length to the greater of 1 and the length of set the likely length to the greater of 1. If MAXBOUND is
the shortest string and reset the lower bound to zero. */ set, also reset the length of the lower bound to zero. */
res.range.likely = res.range.min ? res.range.min : warn_level > 1; res.range.likely = res.range.min ? res.range.min : warn_level > 1;
res.range.min = 0; if (lendata.maxbound)
res.range.min = 0;
} }
res.range.unlikely = unbounded ? HOST_WIDE_INT_MAX : res.range.max; res.range.unlikely = unbounded ? HOST_WIDE_INT_MAX : res.range.max;
...@@ -2110,7 +2062,7 @@ get_string_length (tree str, unsigned eltsize) ...@@ -2110,7 +2062,7 @@ get_string_length (tree str, unsigned eltsize)
vsprinf). */ vsprinf). */
static fmtresult static fmtresult
format_character (const directive &dir, tree arg, vr_values *vr_values) format_character (const directive &dir, tree arg, const vr_values *vr_values)
{ {
fmtresult res; fmtresult res;
...@@ -2186,7 +2138,7 @@ format_character (const directive &dir, tree arg, vr_values *vr_values) ...@@ -2186,7 +2138,7 @@ format_character (const directive &dir, tree arg, vr_values *vr_values)
vsprinf). */ vsprinf). */
static fmtresult static fmtresult
format_string (const directive &dir, tree arg, vr_values *) format_string (const directive &dir, tree arg, const vr_values *vr_values)
{ {
fmtresult res; fmtresult res;
...@@ -2204,7 +2156,7 @@ format_string (const directive &dir, tree arg, vr_values *) ...@@ -2204,7 +2156,7 @@ format_string (const directive &dir, tree arg, vr_values *)
gcc_checking_assert (count_by == 2 || count_by == 4); gcc_checking_assert (count_by == 2 || count_by == 4);
} }
fmtresult slen = get_string_length (arg, count_by); fmtresult slen = get_string_length (arg, count_by, vr_values);
if (slen.range.min == slen.range.max if (slen.range.min == slen.range.max
&& slen.range.min < HOST_WIDE_INT_MAX) && slen.range.min < HOST_WIDE_INT_MAX)
{ {
...@@ -2376,7 +2328,7 @@ format_string (const directive &dir, tree arg, vr_values *) ...@@ -2376,7 +2328,7 @@ format_string (const directive &dir, tree arg, vr_values *)
/* Format plain string (part of the format string itself). */ /* Format plain string (part of the format string itself). */
static fmtresult static fmtresult
format_plain (const directive &dir, tree, vr_values *) format_plain (const directive &dir, tree, const vr_values *)
{ {
fmtresult res (dir.len); fmtresult res (dir.len);
return res; return res;
...@@ -2386,7 +2338,7 @@ format_plain (const directive &dir, tree, vr_values *) ...@@ -2386,7 +2338,7 @@ format_plain (const directive &dir, tree, vr_values *)
should be diagnosed given the AVAILable space in the destination. */ should be diagnosed given the AVAILable space in the destination. */
static bool static bool
should_warn_p (const sprintf_dom_walker::call_info &info, should_warn_p (const call_info &info,
const result_range &avail, const result_range &result) const result_range &avail, const result_range &result)
{ {
if (result.max <= avail.min) if (result.max <= avail.min)
...@@ -2457,7 +2409,7 @@ should_warn_p (const sprintf_dom_walker::call_info &info, ...@@ -2457,7 +2409,7 @@ should_warn_p (const sprintf_dom_walker::call_info &info,
static bool static bool
maybe_warn (substring_loc &dirloc, location_t argloc, maybe_warn (substring_loc &dirloc, location_t argloc,
const sprintf_dom_walker::call_info &info, const call_info &info,
const result_range &avail_range, const result_range &res, const result_range &avail_range, const result_range &res,
const directive &dir) const directive &dir)
{ {
...@@ -2737,9 +2689,9 @@ maybe_warn (substring_loc &dirloc, location_t argloc, ...@@ -2737,9 +2689,9 @@ maybe_warn (substring_loc &dirloc, location_t argloc,
in *RES. Return true if the directive has been handled. */ in *RES. Return true if the directive has been handled. */
static bool static bool
format_directive (const sprintf_dom_walker::call_info &info, format_directive (const call_info &info,
format_result *res, const directive &dir, format_result *res, const directive &dir,
class vr_values *vr_values) const class vr_values *vr_values)
{ {
/* Offset of the beginning of the directive from the beginning /* Offset of the beginning of the directive from the beginning
of the format string. */ of the format string. */
...@@ -3086,10 +3038,10 @@ format_directive (const sprintf_dom_walker::call_info &info, ...@@ -3086,10 +3038,10 @@ format_directive (const sprintf_dom_walker::call_info &info,
the directive. */ the directive. */
static size_t static size_t
parse_directive (sprintf_dom_walker::call_info &info, parse_directive (call_info &info,
directive &dir, format_result *res, directive &dir, format_result *res,
const char *str, unsigned *argno, const char *str, unsigned *argno,
vr_values *vr_values) const vr_values *vr_values)
{ {
const char *pcnt = strchr (str, target_percent); const char *pcnt = strchr (str, target_percent);
dir.beg = str; dir.beg = str;
...@@ -3526,9 +3478,8 @@ parse_directive (sprintf_dom_walker::call_info &info, ...@@ -3526,9 +3478,8 @@ parse_directive (sprintf_dom_walker::call_info &info,
on, false otherwise (e.g., when a unknown or unhandled directive was seen on, false otherwise (e.g., when a unknown or unhandled directive was seen
that caused the processing to be terminated early). */ that caused the processing to be terminated early). */
bool static bool
sprintf_dom_walker::compute_format_length (call_info &info, compute_format_length (call_info &info, format_result *res, const vr_values *vr)
format_result *res)
{ {
if (dump_file) if (dump_file)
{ {
...@@ -3564,12 +3515,10 @@ sprintf_dom_walker::compute_format_length (call_info &info, ...@@ -3564,12 +3515,10 @@ sprintf_dom_walker::compute_format_length (call_info &info,
directive dir = directive (); directive dir = directive ();
dir.dirno = dirno; dir.dirno = dirno;
size_t n = parse_directive (info, dir, res, pf, &argno, size_t n = parse_directive (info, dir, res, pf, &argno, vr);
evrp_range_analyzer.get_vr_values ());
/* Return failure if the format function fails. */ /* Return failure if the format function fails. */
if (!format_directive (info, res, dir, if (!format_directive (info, res, dir, vr))
evrp_range_analyzer.get_vr_values ()))
return false; return false;
/* Return success the directive is zero bytes long and it's /* Return success the directive is zero bytes long and it's
...@@ -3617,7 +3566,7 @@ get_destination_size (tree dest) ...@@ -3617,7 +3566,7 @@ get_destination_size (tree dest)
of its return values. */ of its return values. */
static bool static bool
is_call_safe (const sprintf_dom_walker::call_info &info, is_call_safe (const call_info &info,
const format_result &res, bool under4k, const format_result &res, bool under4k,
unsigned HOST_WIDE_INT retval[2]) unsigned HOST_WIDE_INT retval[2])
{ {
...@@ -3676,7 +3625,7 @@ is_call_safe (const sprintf_dom_walker::call_info &info, ...@@ -3676,7 +3625,7 @@ is_call_safe (const sprintf_dom_walker::call_info &info,
static bool static bool
try_substitute_return_value (gimple_stmt_iterator *gsi, try_substitute_return_value (gimple_stmt_iterator *gsi,
const sprintf_dom_walker::call_info &info, const call_info &info,
const format_result &res) const format_result &res)
{ {
tree lhs = gimple_get_lhs (info.callstmt); tree lhs = gimple_get_lhs (info.callstmt);
...@@ -3794,7 +3743,7 @@ try_substitute_return_value (gimple_stmt_iterator *gsi, ...@@ -3794,7 +3743,7 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
static bool static bool
try_simplify_call (gimple_stmt_iterator *gsi, try_simplify_call (gimple_stmt_iterator *gsi,
const sprintf_dom_walker::call_info &info, const call_info &info,
const format_result &res) const format_result &res)
{ {
unsigned HOST_WIDE_INT dummy[2]; unsigned HOST_WIDE_INT dummy[2];
...@@ -3847,13 +3796,17 @@ get_user_idx_format (tree fndecl, unsigned *idx_args) ...@@ -3847,13 +3796,17 @@ get_user_idx_format (tree fndecl, unsigned *idx_args)
return tree_to_uhwi (fmtarg) - 1; return tree_to_uhwi (fmtarg) - 1;
} }
/* Determine if a GIMPLE CALL is to one of the sprintf-like built-in } /* Unnamed namespace. */
functions and if so, handle it. Return true if the call is removed
and gsi_next should not be performed in the caller. */ /* Determine if a GIMPLE call at *GSI is to one of the sprintf-like built-in
functions and if so, handle it. Return true if the call is removed and
gsi_next should not be performed in the caller. */
bool bool
sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) handle_printf_call (gimple_stmt_iterator *gsi, const vr_values *vr_values)
{ {
init_target_to_host_charmap ();
call_info info = call_info (); call_info info = call_info ();
info.callstmt = gsi_stmt (*gsi); info.callstmt = gsi_stmt (*gsi);
...@@ -4119,7 +4072,9 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -4119,7 +4072,9 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
/* Try to determine the range of values of the argument /* Try to determine the range of values of the argument
and use the greater of the two at level 1 and the smaller and use the greater of the two at level 1 and the smaller
of them at level 2. */ of them at level 2. */
const value_range *vr = evrp_range_analyzer.get_value_range (size); const value_range *vr
= CONST_CAST (class vr_values *, vr_values)->get_value_range (size);
if (range_int_cst_p (vr)) if (range_int_cst_p (vr))
{ {
unsigned HOST_WIDE_INT minsize = TREE_INT_CST_LOW (vr->min ()); unsigned HOST_WIDE_INT minsize = TREE_INT_CST_LOW (vr->min ());
...@@ -4230,7 +4185,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -4230,7 +4185,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
never set to true again). */ never set to true again). */
res.posunder4k = posunder4k && dstptr; res.posunder4k = posunder4k && dstptr;
bool success = compute_format_length (info, &res); bool success = compute_format_length (info, &res, vr_values);
if (res.warned) if (res.warned)
gimple_set_no_warning (info.callstmt, true); gimple_set_no_warning (info.callstmt, true);
...@@ -4256,71 +4211,3 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi) ...@@ -4256,71 +4211,3 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
return call_removed; return call_removed;
} }
edge
sprintf_dom_walker::before_dom_children (basic_block bb)
{
evrp_range_analyzer.enter (bb);
for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si); )
{
/* Iterate over statements, looking for function calls. */
gimple *stmt = gsi_stmt (si);
/* First record ranges generated by this statement. */
evrp_range_analyzer.record_ranges_from_stmt (stmt, false);
if (is_gimple_call (stmt) && handle_gimple_call (&si))
/* If handle_gimple_call returns true, the iterator is
already pointing to the next statement. */
continue;
gsi_next (&si);
}
return NULL;
}
void
sprintf_dom_walker::after_dom_children (basic_block bb)
{
evrp_range_analyzer.leave (bb);
}
/* Execute the pass for function FUN. */
unsigned int
pass_sprintf_length::execute (function *fun)
{
init_target_to_host_charmap ();
calculate_dominance_info (CDI_DOMINATORS);
bool use_scev = optimize > 0 && flag_printf_return_value;
if (use_scev)
{
loop_optimizer_init (LOOPS_NORMAL);
scev_initialize ();
}
sprintf_dom_walker sprintf_dom_walker;
sprintf_dom_walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
if (use_scev)
{
scev_finalize ();
loop_optimizer_finalize ();
}
/* Clean up object size info. */
fini_object_sizes ();
return 0;
}
} /* Unnamed namespace. */
/* Return a pointer to a pass object newly constructed from the context
CTXT. */
gimple_opt_pass *
make_pass_sprintf_length (gcc::context *ctxt)
{
return new pass_sprintf_length (ctxt);
}
...@@ -42,7 +42,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -42,7 +42,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_build_cfg); NEXT_PASS (pass_build_cfg);
NEXT_PASS (pass_warn_function_return); NEXT_PASS (pass_warn_function_return);
NEXT_PASS (pass_expand_omp); NEXT_PASS (pass_expand_omp);
NEXT_PASS (pass_sprintf_length, false); NEXT_PASS (pass_warn_printf);
NEXT_PASS (pass_walloca, /*strict_mode_p=*/true); NEXT_PASS (pass_walloca, /*strict_mode_p=*/true);
NEXT_PASS (pass_build_cgraph_edges); NEXT_PASS (pass_build_cgraph_edges);
TERMINATE_PASS_LIST (all_lowering_passes) TERMINATE_PASS_LIST (all_lowering_passes)
...@@ -307,7 +307,6 @@ along with GCC; see the file COPYING3. If not see ...@@ -307,7 +307,6 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_lower_vector_ssa); NEXT_PASS (pass_lower_vector_ssa);
NEXT_PASS (pass_lower_switch); NEXT_PASS (pass_lower_switch);
NEXT_PASS (pass_cse_reciprocals); NEXT_PASS (pass_cse_reciprocals);
NEXT_PASS (pass_sprintf_length, true);
NEXT_PASS (pass_reassoc, false /* insert_powi_p */); NEXT_PASS (pass_reassoc, false /* insert_powi_p */);
NEXT_PASS (pass_strength_reduction); NEXT_PASS (pass_strength_reduction);
NEXT_PASS (pass_split_paths); NEXT_PASS (pass_split_paths);
...@@ -358,7 +357,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -358,7 +357,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_object_sizes); NEXT_PASS (pass_object_sizes);
/* Fold remaining builtins. */ /* Fold remaining builtins. */
NEXT_PASS (pass_fold_builtins); NEXT_PASS (pass_fold_builtins);
NEXT_PASS (pass_sprintf_length, true); NEXT_PASS (pass_strlen);
/* Copy propagation also copy-propagates constants, this is necessary /* Copy propagation also copy-propagates constants, this is necessary
to forward object-size and builtin folding results properly. */ to forward object-size and builtin folding results properly. */
NEXT_PASS (pass_copy_prop); NEXT_PASS (pass_copy_prop);
......
...@@ -1815,7 +1815,7 @@ print_pattern (pretty_printer *pp, const_rtx x, int verbose) ...@@ -1815,7 +1815,7 @@ print_pattern (pretty_printer *pp, const_rtx x, int verbose)
gcc_assert (strlen (print_rtx_head) < sizeof (indented_print_rtx_head) - 4); gcc_assert (strlen (print_rtx_head) < sizeof (indented_print_rtx_head) - 4);
snprintf (indented_print_rtx_head, snprintf (indented_print_rtx_head,
sizeof (indented_print_rtx_head), sizeof (indented_print_rtx_head),
"%s ", print_rtx_head); "%s ", print_rtx_head);
print_rtx_head = indented_print_rtx_head; print_rtx_head = indented_print_rtx_head;
for (int i = 0; i < seq->len (); i++) for (int i = 0; i < seq->len (); i++)
print_insn_with_notes (pp, seq->insn (i)); print_insn_with_notes (pp, seq->insn (i));
......
...@@ -79,6 +79,76 @@ ...@@ -79,6 +79,76 @@
2019-08-23 Martin Sebor <msebor@redhat.com> 2019-08-23 Martin Sebor <msebor@redhat.com>
PR c++/83431
* gcc.dg/strlenopt-63.c: New test.
* gcc.dg/pr79538.c: Adjust text of expected warning.
* gcc.dg/pr81292-1.c: Adjust pass name.
* gcc.dg/pr81292-2.c: Same.
* gcc.dg/pr81703.c: Same.
* gcc.dg/strcmpopt_2.c: Same.
* gcc.dg/strcmpopt_3.c: Same.
* gcc.dg/strcmpopt_4.c: Same.
* gcc.dg/strlenopt-1.c: Same.
* gcc.dg/strlenopt-10.c: Same.
* gcc.dg/strlenopt-11.c: Same.
* gcc.dg/strlenopt-13.c: Same.
* gcc.dg/strlenopt-14g.c: Same.
* gcc.dg/strlenopt-14gf.c: Same.
* gcc.dg/strlenopt-15.c: Same.
* gcc.dg/strlenopt-16g.c: Same.
* gcc.dg/strlenopt-17g.c: Same.
* gcc.dg/strlenopt-18g.c: Same.
* gcc.dg/strlenopt-19.c: Same.
* gcc.dg/strlenopt-1f.c: Same.
* gcc.dg/strlenopt-2.c: Same.
* gcc.dg/strlenopt-20.c: Same.
* gcc.dg/strlenopt-21.c: Same.
* gcc.dg/strlenopt-22.c: Same.
* gcc.dg/strlenopt-22g.c: Same.
* gcc.dg/strlenopt-24.c: Same.
* gcc.dg/strlenopt-25.c: Same.
* gcc.dg/strlenopt-26.c: Same.
* gcc.dg/strlenopt-27.c: Same.
* gcc.dg/strlenopt-28.c: Same.
* gcc.dg/strlenopt-29.c: Same.
* gcc.dg/strlenopt-2f.c: Same.
* gcc.dg/strlenopt-3.c: Same.
* gcc.dg/strlenopt-30.c: Same.
* gcc.dg/strlenopt-31g.c: Same.
* gcc.dg/strlenopt-32.c: Same.
* gcc.dg/strlenopt-33.c: Same.
* gcc.dg/strlenopt-33g.c: Same.
* gcc.dg/strlenopt-34.c: Same.
* gcc.dg/strlenopt-35.c: Same.
* gcc.dg/strlenopt-4.c: Same.
* gcc.dg/strlenopt-48.c: Same.
* gcc.dg/strlenopt-49.c: Same.
* gcc.dg/strlenopt-4g.c: Same.
* gcc.dg/strlenopt-4gf.c: Same.
* gcc.dg/strlenopt-5.c: Same.
* gcc.dg/strlenopt-50.c: Same.
* gcc.dg/strlenopt-51.c: Same.
* gcc.dg/strlenopt-52.c: Same.
* gcc.dg/strlenopt-53.c: Same.
* gcc.dg/strlenopt-54.c: Same.
* gcc.dg/strlenopt-55.c: Same.
* gcc.dg/strlenopt-56.c: Same.
* gcc.dg/strlenopt-6.c: Same.
* gcc.dg/strlenopt-61.c: Same.
* gcc.dg/strlenopt-7.c: Same.
* gcc.dg/strlenopt-8.c: Same.
* gcc.dg/strlenopt-9.c: Same.
* gcc.dg/strlenopt.h (snprintf, snprintf): Declare.
* gcc.dg/tree-ssa/builtin-snprintf-6.c: New test.
* gcc.dg/tree-ssa/builtin-snprintf-7.c: New test.
* gcc.dg/tree-ssa/builtin-snprintf-8.c: New test.
* gcc.dg/tree-ssa/builtin-snprintf-9.c: New test.
* gcc.dg/tree-ssa/builtin-sprintf-warn-21.c: New test.
* gcc.dg/tree-ssa/dump-4.c: New test.
* gcc.dg/tree-ssa/pr83501.c: Adjust pass name.
2019-08-23 Martin Sebor <msebor@redhat.com>
* gcc.dg/Warray-bounds-36.c: Make functions static to avoid failures * gcc.dg/Warray-bounds-36.c: Make functions static to avoid failures
with -fpic. with -fpic.
* gcc.dg/Warray-bounds-41.c: Same. * gcc.dg/Warray-bounds-41.c: Same.
......
...@@ -17,6 +17,6 @@ void f () ...@@ -17,6 +17,6 @@ void f ()
{ {
char des[3]; char des[3];
char src[] = "abcd"; char src[] = "abcd";
__builtin_sprintf (des, "%s", src); /* { dg-warning "directive writing up to 4 bytes into a region of size 3" } */ __builtin_sprintf (des, "%s", src); /* { dg-warning "directive writing 4 bytes into a region of size 3" } */
return; return;
} }
...@@ -32,4 +32,4 @@ main (void) ...@@ -32,4 +32,4 @@ main (void)
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
...@@ -32,4 +32,4 @@ main (void) ...@@ -32,4 +32,4 @@ main (void)
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 6 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 6 "strlen1" } } */
...@@ -9,4 +9,4 @@ unsigned g (void) ...@@ -9,4 +9,4 @@ unsigned g (void)
return __builtin_strlen (d); return __builtin_strlen (d);
} }
/* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen" } } */ /* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen1" } } */
...@@ -64,4 +64,4 @@ int main (void) ...@@ -64,4 +64,4 @@ int main (void)
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 8 "strlen" } } */ /* { dg-final { scan-tree-dump-times "cmp_eq \\(" 8 "strlen1" } } */
...@@ -28,4 +28,4 @@ int main (void) ...@@ -28,4 +28,4 @@ int main (void)
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strcmp" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcmp" 0 "strlen1" } } */
...@@ -13,4 +13,4 @@ f1 (S * s) ...@@ -13,4 +13,4 @@ f1 (S * s)
return result; return result;
} }
/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "cmp_eq \\(" 1 "strlen1" } } */
...@@ -36,9 +36,9 @@ main () ...@@ -36,9 +36,9 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -69,14 +69,14 @@ main () ...@@ -69,14 +69,14 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
/* avr has BIGGEST_ALIGNMENT 8, allowing fold_builtin_memory_op /* avr has BIGGEST_ALIGNMENT 8, allowing fold_builtin_memory_op
to expand the memcpy call at the end of fn2. */ to expand the memcpy call at the end of fn2. */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 8 "strlen" { target { ! avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 8 "strlen1" { target { ! avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen" { target { avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" { target { avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "\\*q_\[0-9\]* = 32;" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "\\*q_\[0-9\]* = 32;" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(\[^\n\r\]*, 1\\)" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(\[^\n\r\]*, 1\\)" 1 "strlen1" } } */
...@@ -58,18 +58,18 @@ main () ...@@ -58,18 +58,18 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen1" } } */
/* avr has BIGGEST_ALIGNMENT 8, allowing fold_builtin_memory_op /* avr has BIGGEST_ALIGNMENT 8, allowing fold_builtin_memory_op
to expand the memcpy call at the end of fn1. */ to expand the memcpy call at the end of fn1. */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen" { target { ! avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" { target { ! avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" { target { avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" { target { avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
/* Where the memcpy is expanded, the assignemts to elements of l are /* Where the memcpy is expanded, the assignemts to elements of l are
propagated. */ propagated. */
/* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.0. = " 1 "strlen" { target { ! avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.0. = " 1 "strlen1" { target { ! avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.6. = " 1 "strlen" { target { ! avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.6. = " 1 "strlen1" { target { ! avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.9. = " 1 "strlen" { target { ! avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.9. = " 1 "strlen1" { target { ! avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;" 3 "strlen" { target { avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;" 3 "strlen1" { target { avr-*-* } } } } */
...@@ -55,19 +55,19 @@ main () ...@@ -55,19 +55,19 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen1" } } */
/* avr has BIGGEST_ALIGNMENT 8, allowing fold_builtin_memory_op /* avr has BIGGEST_ALIGNMENT 8, allowing fold_builtin_memory_op
to expand the memcpy call at the end of fn1. */ to expand the memcpy call at the end of fn1. */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen" { target { ! avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" { target { ! avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" { target { avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" { target { avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
/* Where the memcpy is expanded, the assignemts to elements of l are /* Where the memcpy is expanded, the assignemts to elements of l are
propagated. */ propagated. */
/* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.0. = " 1 "strlen" { target { ! avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.0. = " 1 "strlen1" { target { ! avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.1. = " 1 "strlen" { target { ! avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.1. = " 1 "strlen1" { target { ! avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.5. = " 1 "strlen" { target { ! avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.5. = " 1 "strlen1" { target { ! avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.6. = " 1 "strlen" { target { ! avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]* l.6. = " 1 "strlen1" { target { ! avr-*-* } } } } */
/* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;" 4 "strlen" { target { avr-*-* } } } } */ /* { dg-final { scan-tree-dump-times " _\[0-9\]* = strlen \\(\[^\n\r\]*;" 4 "strlen1" { target { avr-*-* } } } } */
...@@ -107,10 +107,10 @@ main () ...@@ -107,10 +107,10 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
...@@ -11,15 +11,15 @@ ...@@ -11,15 +11,15 @@
/* Compared to strlenopt-14gf.c, strcpy_chk with string literal as /* Compared to strlenopt-14gf.c, strcpy_chk with string literal as
second argument isn't being optimized by builtins.c into second argument isn't being optimized by builtins.c into
memcpy. */ memcpy. */
/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__mempcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__mempcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
...@@ -51,9 +51,9 @@ main () ...@@ -51,9 +51,9 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -24,10 +24,10 @@ main () ...@@ -24,10 +24,10 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen1" } } */
...@@ -47,10 +47,10 @@ main () ...@@ -47,10 +47,10 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen1" } } */
...@@ -73,9 +73,9 @@ main () ...@@ -73,9 +73,9 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
...@@ -72,9 +72,9 @@ main () ...@@ -72,9 +72,9 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -5,13 +5,13 @@ ...@@ -5,13 +5,13 @@
#define FORTIFY_SOURCE 2 #define FORTIFY_SOURCE 2
#include "strlenopt-1.c" #include "strlenopt-1.c"
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -40,9 +40,9 @@ main () ...@@ -40,9 +40,9 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 5 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 5 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -86,9 +86,9 @@ main () ...@@ -86,9 +86,9 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -57,9 +57,9 @@ main () ...@@ -57,9 +57,9 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -31,9 +31,9 @@ main () ...@@ -31,9 +31,9 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
#define USE_GNU #define USE_GNU
#include "strlenopt-22.c" #include "strlenopt-22.c"
/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen1" } } */
...@@ -13,4 +13,4 @@ main () ...@@ -13,4 +13,4 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
...@@ -14,4 +14,4 @@ main () ...@@ -14,4 +14,4 @@ main ()
return len - len2; return len - len2;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
...@@ -20,5 +20,5 @@ main (void) ...@@ -20,5 +20,5 @@ main (void)
return fn1 (p, q); return fn1 (p, q);
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
...@@ -19,4 +19,4 @@ main (void) ...@@ -19,4 +19,4 @@ main (void)
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
...@@ -56,4 +56,4 @@ main () ...@@ -56,4 +56,4 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
...@@ -24,4 +24,4 @@ main () ...@@ -24,4 +24,4 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
...@@ -5,13 +5,13 @@ ...@@ -5,13 +5,13 @@
#define FORTIFY_SOURCE 2 #define FORTIFY_SOURCE 2
#include "strlenopt-2.c" #include "strlenopt-2.c"
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 5 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 5 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -53,12 +53,12 @@ main () ...@@ -53,12 +53,12 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "return 0" 3 "optimized" } } */ /* { dg-final { scan-tree-dump-times "return 0" 3 "optimized" } } */
/* { dg-final { scan-tree-dump-times "return 4" 1 "optimized" } } */ /* { dg-final { scan-tree-dump-times "return 4" 1 "optimized" } } */
/* { dg-final { scan-tree-dump-times "return 3" 1 "optimized" } } */ /* { dg-final { scan-tree-dump-times "return 3" 1 "optimized" } } */
...@@ -60,4 +60,4 @@ _Bool f7(char *s) ...@@ -60,4 +60,4 @@ _Bool f7(char *s)
return (t1 == s); return (t1 == s);
} }
/* { dg-final { scan-tree-dump-times "__builtin_strncmp" 5 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__builtin_strncmp" 5 "strlen1" } } */
...@@ -4,6 +4,6 @@ ...@@ -4,6 +4,6 @@
#define USE_GNU #define USE_GNU
#include "strlenopt-31.c" #include "strlenopt-31.c"
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-not "strlen \\(" "strlen" } } */ /* { dg-final { scan-tree-dump-not "strlen \\(" "strlen1" } } */
...@@ -190,4 +190,4 @@ main () ...@@ -190,4 +190,4 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
...@@ -39,4 +39,4 @@ main () ...@@ -39,4 +39,4 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
...@@ -40,5 +40,5 @@ main () ...@@ -40,5 +40,5 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
...@@ -35,4 +35,4 @@ main () ...@@ -35,4 +35,4 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
...@@ -28,4 +28,4 @@ main () ...@@ -28,4 +28,4 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
...@@ -66,9 +66,9 @@ main () ...@@ -66,9 +66,9 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 3 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 3 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 3 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 3 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -31,5 +31,5 @@ void h (void) ...@@ -31,5 +31,5 @@ void h (void)
abort(); abort();
} }
/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } } /* { dg-final { scan-tree-dump-times "strlen1" 0 "optimized" } }
{ dg-final { scan-tree-dump-times "abort" 0 "optimized" } } */ { dg-final { scan-tree-dump-times "abort" 0 "optimized" } } */
...@@ -45,7 +45,7 @@ int cmp88 (void) ...@@ -45,7 +45,7 @@ int cmp88 (void)
return cmp88; return cmp88;
} }
/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } /* { dg-final { scan-tree-dump-times "strlen1" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "len0 = 0;" 1 "gimple" } } { dg-final { scan-tree-dump-times "len0 = 0;" 1 "gimple" } }
{ dg-final { scan-tree-dump-times "len = 18;" 1 "gimple" } } { dg-final { scan-tree-dump-times "len = 18;" 1 "gimple" } }
{ dg-final { scan-tree-dump-times "lenx = 8;" 1 "gimple" } } { dg-final { scan-tree-dump-times "lenx = 8;" 1 "gimple" } }
......
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
#define USE_GNU #define USE_GNU
#include "strlenopt-4.c" #include "strlenopt-4.c"
/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen1" } } */
...@@ -6,13 +6,13 @@ ...@@ -6,13 +6,13 @@
#define FORTIFY_SOURCE 2 #define FORTIFY_SOURCE 2
#include "strlenopt-4.c" #include "strlenopt-4.c"
/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen1" } } */
...@@ -48,9 +48,9 @@ main () ...@@ -48,9 +48,9 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -112,5 +112,5 @@ void test_array_ref (void) ...@@ -112,5 +112,5 @@ void test_array_ref (void)
T (&b[16], 0); T (&b[17], 0); T (&b[18], 0); T (&b[19], 0); T (&b[16], 0); T (&b[17], 0); T (&b[18], 0); T (&b[19], 0);
} }
/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } /* { dg-final { scan-tree-dump-times "strlen1" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */ { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */
...@@ -84,4 +84,4 @@ void test_elim_a9_9 (unsigned i) ...@@ -84,4 +84,4 @@ void test_elim_a9_9 (unsigned i)
T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8); T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8);
} }
/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } */ /* { dg-final { scan-tree-dump-times "strlen1" 0 "gimple" } } */
...@@ -284,5 +284,5 @@ void test_global_struct_struct_array (void) ...@@ -284,5 +284,5 @@ void test_global_struct_struct_array (void)
T (ssa[5].sa9[3].a6, 3); T (ssa[5].sa9[3].a6, 3);
} }
/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } /* { dg-final { scan-tree-dump-times "strlen1" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */ { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */
...@@ -112,5 +112,5 @@ void test_array_ref (void) ...@@ -112,5 +112,5 @@ void test_array_ref (void)
T (&b[16], 0); T (&b[17], 0); T (&b[18], 0); T (&b[19], 0); T (&b[16], 0); T (&b[17], 0); T (&b[18], 0); T (&b[19], 0);
} }
/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } /* { dg-final { scan-tree-dump-times "strlen1" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */ { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */
...@@ -105,5 +105,5 @@ void elim_after_init_memcpy (void) ...@@ -105,5 +105,5 @@ void elim_after_init_memcpy (void)
T ("AB\000CD", 0, "ab\000c", 4, 2); T ("AB\000CD", 0, "ab\000c", 4, 2);
} }
/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } } /* { dg-final { scan-tree-dump-times "strlen1" 0 "optimized" } }
{ dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "optimized" } } */ { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "optimized" } } */
...@@ -224,7 +224,7 @@ const void test_large_string_size (void) ...@@ -224,7 +224,7 @@ const void test_large_string_size (void)
} }
/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } /* { dg-final { scan-tree-dump-times "strlen1" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "memcmp" 0 "gimple" } } { dg-final { scan-tree-dump-times "memcmp" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "strcmp" 0 "gimple" } } { dg-final { scan-tree-dump-times "strcmp" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "optimized" } } */ { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "optimized" } } */
...@@ -45,6 +45,6 @@ void test_contents (void) ...@@ -45,6 +45,6 @@ void test_contents (void)
} }
/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } /* { dg-final { scan-tree-dump-times "strlen1" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "strcmp" 0 "gimple" } } { dg-final { scan-tree-dump-times "strcmp" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "abort" 0 "optimized" } } */ { dg-final { scan-tree-dump-times "abort" 0 "optimized" } } */
...@@ -77,9 +77,9 @@ main () ...@@ -77,9 +77,9 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
...@@ -215,4 +215,4 @@ void test_ta2 (void) ...@@ -215,4 +215,4 @@ void test_ta2 (void)
} }
/* { dg-final { scan-tree-dump-not "failure" "optimized" } } /* { dg-final { scan-tree-dump-not "failure" "optimized" } }
{ dg-final { scan-tree-dump-not "strlen" "gimple" } } */ { dg-final { scan-tree-dump-not "strlen1" "gimple" } } */
/* PR tree-optimization/83431 - Verify that snprintf (0, 0, "%s",
with an argument that's a conditional expression evaluates to
the expected result regardless of the order of the expression
operands.
{ dg-do run }
{ dg-options "-O2 -Wall" } */
#include "strlenopt.h"
#define A(expr) \
((expr) \
? (void)0 \
: (__builtin_printf ("assertion failed on line %i: %s\n", \
__LINE__, #expr), \
__builtin_abort ()))
const char gs0[] = "";
const char gs3[] = "123";
char gc;
char ga5[7];
struct S { char n, ma7[7], max[]; };
__attribute__ ((noclone, noinline, noipa)) void
equal_4_gs0_gs3_ga5_m1 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? gs0 : 0 < i ? gs3 : ga5;
A (snprintf (0, 0, "%s", p) == 0);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_gs0_gs3_ga5_0 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? gs0 : 0 < i ? gs3 : ga5;
A (snprintf (0, 0, "%s", p) == 4);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_gs0_gs3_ga5_p1 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? gs0 : 0 < i ? gs3 : ga5;
A (snprintf (0, 0, "%s", p) == 3);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_gs0_ga5_gs3_m1 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? gs0 : 0 < i ? ga5 : gs3;
A (snprintf (0, 0, "%s", p) == 0);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_gs0_ga5_gs3_0 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? gs0 : 0 < i ? ga5 : gs3;
A (snprintf (0, 0, "%s", p) == 3);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_gs0_ga5_gs3_p1 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? gs0 : 0 < i ? ga5 : gs3;
A (snprintf (0, 0, "%s", p) == 4);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_ga5_gs0_gs3_m1 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? ga5 : 0 < i ? gs0 : gs3;
A (snprintf (0, 0, "%s", p) == 4);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_ga5_gs0_gs3_0 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? ga5 : 0 < i ? gs0 : gs3;
A (snprintf (0, 0, "%s", p) == 3);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_ga5_gs0_gs3_p1 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? ga5 : 0 < i ? gs0 : gs3;
A (snprintf (0, 0, "%s", p) == 0);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_ga5_gs3_gs0_m1 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? ga5 : 0 < i ? gs3 : gs0;
A (snprintf (0, 0, "%s", p) == 4);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_ga5_gs3_gs0_0 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? ga5 : 0 < i ? gs3 : gs0;
A (snprintf (0, 0, "%s", p) == 0);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_ga5_gs3_gs0_p1 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? ga5 : 0 < i ? gs3 : gs0;
A (snprintf (0, 0, "%s", p) == 3);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_gs3_gs0_ga5_m1 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? gs3 : 0 < i ? gs0 : ga5;
A (snprintf (0, 0, "%s", p) == 3);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_gs3_gs0_ga5_0 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? gs3 : 0 < i ? gs0 : ga5;
A (snprintf (0, 0, "%s", p) == 4);
}
__attribute__ ((noclone, noinline, noipa)) void
equal_4_gs3_gs0_ga5_p1 (int i)
{
strcpy (ga5, "1234");
const char *p = i < 0 ? gs3 : 0 < i ? gs0 : ga5;
A (snprintf (0, 0, "%s", p) == 0);
}
/* Similar to the above but with memcpy creating a string at least
four characters long, and the address of the NUL character. */
__attribute__ ((noclone, noinline, noipa)) void
min_4_gc_gs3_ga5_m1 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? &gc : 0 < i ? gs3 : ga5;
A (snprintf (0, 0, "%s", p) == 0);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_gc_gs3_ga5_0 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? &gc : 0 < i ? gs3 : ga5;
A (snprintf (0, 0, "%s", p) == 4);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_gc_gs3_ga5_p1 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? &gc : 0 < i ? gs3 : ga5;
A (snprintf (0, 0, "%s", p) == 3);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_gc_ga5_gs3_m1 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? &gc : 0 < i ? ga5 : gs3;
A (snprintf (0, 0, "%s", p) == 0);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_gc_ga5_gs3_0 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? &gc : 0 < i ? ga5 : gs3;
A (snprintf (0, 0, "%s", p) == 3);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_gc_ga5_gs3_p1 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? &gc : 0 < i ? ga5 : gs3;
A (snprintf (0, 0, "%s", p) == 4);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_ga5_gc_gs3_m1 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? ga5 : 0 < i ? &gc : gs3;
A (snprintf (0, 0, "%s", p) == 4);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_ga5_gc_gs3_0 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? ga5 : 0 < i ? &gc : gs3;
A (snprintf (0, 0, "%s", p) == 3);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_ga5_gc_gs3_p1 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? ga5 : 0 < i ? &gc : gs3;
A (snprintf (0, 0, "%s", p) == 0);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_ga5_gs3_gc_m1 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? ga5 : 0 < i ? gs3 : &gc;
A (snprintf (0, 0, "%s", p) == 4);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_ga5_gs3_gc_0 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? ga5 : 0 < i ? gs3 : &gc;
A (snprintf (0, 0, "%s", p) == 0);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_ga5_gs3_gc_p1 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? ga5 : 0 < i ? gs3 : &gc;
A (snprintf (0, 0, "%s", p) == 3);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_gs3_gc_ga5_m1 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? gs3 : 0 < i ? &gc : ga5;
A (snprintf (0, 0, "%s", p) == 3);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_gs3_gc_ga5_0 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? gs3 : 0 < i ? &gc : ga5;
A (snprintf (0, 0, "%s", p) == 4);
}
__attribute__ ((noclone, noinline, noipa)) void
min_4_gs3_gc_ga5_p1 (int i)
{
gc = 0;
memcpy (ga5, "1234", 4);
const char *p = i < 0 ? gs3 : 0 < i ? &gc : ga5;
A (snprintf (0, 0, "%s", p) == 0);
}
int main (void)
{
equal_4_gs0_gs3_ga5_m1 (-1);
equal_4_gs0_gs3_ga5_0 ( 0);
equal_4_gs0_gs3_ga5_p1 (+1);
equal_4_gs0_ga5_gs3_m1 (-1);
equal_4_gs0_ga5_gs3_0 ( 0);
equal_4_gs0_ga5_gs3_p1 (+1);
equal_4_ga5_gs0_gs3_m1 (-1);
equal_4_ga5_gs0_gs3_0 ( 0);
equal_4_ga5_gs0_gs3_p1 (+1);
equal_4_ga5_gs3_gs0_m1 (-1);
equal_4_ga5_gs3_gs0_0 ( 0);
equal_4_ga5_gs3_gs0_p1 (+1);
equal_4_gs3_gs0_ga5_m1 (-1);
equal_4_gs3_gs0_ga5_0 ( 0);
equal_4_gs3_gs0_ga5_p1 (+1);
/* Same as aabove but with memcpy creating a string at least four
characters long. */
memset (ga5, 0, sizeof ga5);
min_4_gc_gs3_ga5_m1 (-1);
memset (ga5, 0, sizeof ga5);
min_4_gc_gs3_ga5_0 ( 0);
memset (ga5, 0, sizeof ga5);
min_4_gc_gs3_ga5_p1 (+1);
memset (ga5, 0, sizeof ga5);
min_4_gc_ga5_gs3_m1 (-1);
memset (ga5, 0, sizeof ga5);
min_4_gc_ga5_gs3_0 ( 0);
memset (ga5, 0, sizeof ga5);
min_4_gc_ga5_gs3_p1 (+1);
memset (ga5, 0, sizeof ga5);
min_4_ga5_gc_gs3_m1 (-1);
memset (ga5, 0, sizeof ga5);
min_4_ga5_gc_gs3_0 ( 0);
memset (ga5, 0, sizeof ga5);
min_4_ga5_gc_gs3_p1 (+1);
memset (ga5, 0, sizeof ga5);
min_4_ga5_gs3_gc_m1 (-1);
memset (ga5, 0, sizeof ga5);
min_4_ga5_gs3_gc_0 ( 0);
memset (ga5, 0, sizeof ga5);
min_4_ga5_gs3_gc_p1 (+1);
memset (ga5, 0, sizeof ga5);
min_4_gs3_gc_ga5_m1 (-1);
memset (ga5, 0, sizeof ga5);
min_4_gs3_gc_ga5_0 ( 0);
memset (ga5, 0, sizeof ga5);
min_4_gs3_gc_ga5_p1 (+1);
}
...@@ -40,12 +40,12 @@ main () ...@@ -40,12 +40,12 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "\\*r_\[0-9\]* = 0;" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "\\*r_\[0-9\]* = 0;" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "return 3;" 1 "optimized" } } */ /* { dg-final { scan-tree-dump-times "return 3;" 1 "optimized" } } */
/* { dg-final { scan-tree-dump-times "return 0;" 2 "optimized" } } */ /* { dg-final { scan-tree-dump-times "return 0;" 2 "optimized" } } */
...@@ -98,10 +98,10 @@ main () ...@@ -98,10 +98,10 @@ main ()
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "strlen \\(" 5 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen \\(" 5 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" } } */ /* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
/* { dg-final { scan-tree-dump-times "return 4;" 1 "optimized" } } */ /* { dg-final { scan-tree-dump-times "return 4;" 1 "optimized" } } */
/* This is a replacement of needed parts from stdlib.h and string.h /* This is a replacement of needed parts from <stdlib.h> and <string.h>
for -foptimize-strlen testing, to ensure we are testing the builtins for -foptimize-strlen testing, to ensure we are testing the builtins
rather than whatever the OS has in its headers. */ rather than whatever the OS has in its headers. */
...@@ -25,6 +25,9 @@ void *mempcpy (void *__restrict, const void *__restrict, size_t); ...@@ -25,6 +25,9 @@ void *mempcpy (void *__restrict, const void *__restrict, size_t);
char *stpcpy (char *__restrict, const char *__restrict); char *stpcpy (char *__restrict, const char *__restrict);
#endif #endif
int sprintf (char * __restrict, const char *__restrict, ...);
int snprintf (char * __restrict, size_t, const char *__restrict, ...);
#if defined(FORTIFY_SOURCE) && FORTIFY_SOURCE > 0 && __OPTIMIZE__ #if defined(FORTIFY_SOURCE) && FORTIFY_SOURCE > 0 && __OPTIMIZE__
# define bos(ptr) __builtin_object_size (ptr, FORTIFY_SOURCE > 0) # define bos(ptr) __builtin_object_size (ptr, FORTIFY_SOURCE > 0)
# define bos0(ptr) __builtin_object_size (ptr, 0) # define bos0(ptr) __builtin_object_size (ptr, 0)
......
/* Test to verify that snprintf can determine the length of a dynamically
constructed string argument and fold the result into a constant.
{ dg-do compile }
{ dg-options "-O2 -Wall -fdump-tree-optimized" } */
typedef __SIZE_TYPE__ size_t;
char* strcpy (char * restrict, const char * restrict);
int sprintf (char * restrict, const char *restrict, ...);
int snprintf (char * restrict, size_t, const char *restrict, ...);
#define CONCAT(x, y) x ## y
#define CAT(x, y) CONCAT (x, y)
#define FAILNAME(name, counter) \
CAT (CAT (CAT (call_ ## name ##_on_line_, __LINE__), _), counter)
#define FAIL(name, counter) do { \
extern void FAILNAME (name, counter) (void); \
FAILNAME (name, counter)(); \
} while (0)
/* Macro to emit a call to funcation named
call_in_true_branch_not_eliminated_on_line_NNN()
for each call that's expected to be eliminated. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that no such call appears in output. */
#define ELIM(expr) \
if (!(expr)) FAIL (in_true_branch_not_eliminated, __COUNTER__); else (void)0
#define ARGS(...) __VA_ARGS__
#define T(expect, init, fmt, ...) \
do { \
char a[] = init; \
ELIM (expect == snprintf (0, 0, fmt, __VA_ARGS__)); \
} while (0)
/* Exercise a non-const local char array initialized by a string literal. */
void test_assign_string (void)
{
T (0, "", "%s", a);
T (1, "1", "%s", a);
T (4, "1234", "%s", a);
T (5, "123", "s=%s", a);
T (5, "1234", "s=%s", a + 1);
T (2, "1234", "s=%s", a + 4);
T (5, "12345", "s=%s", &a[2]);
T (5, "123456", "s=%.*s", 3, &a[2]);
}
/* Exercise a non-const local char array initialized by an initializer
list. */
void test_assign_init_list (void)
{
T (0, ARGS ({ 0 }), "%s", a);
T (1, ARGS ({ 1, 0 }), "%s", a);
T (3, ARGS ({ [3] = 0, [1] = 2, [0] = 1, [2] = 3 }), "%s", a);
T (3, ARGS ({ [3] = 0, [1] = 2, [0] = 1, [2] = 3, [4] = 0 }), "%s", a);
T (4, ARGS ({ 1, 2, 3, 4, 0 }), "%s", a);
T (5, ARGS ({ 1, 2, 3, 0 }), "s=%s", a);
T (5, ARGS ({ 1, 2, 3, 4, 0 }), "s=%s", a + 1);
T (2, ARGS ({ 1, 2, 3, 4, 0 }), "s=%s", a + 4);
T (5, ARGS ({ 1, 2, 3, 4, 5, 0 }), "s=%s", &a[2]);
T (5, ARGS ({ 1, 2, 3, 4, 5, 6, 0 }), "s=%.*s", 3, &a[2]);
}
#undef T
#define T(expect, init, fmt, ...) \
do { \
struct { int n; char a[sizeof init]; } \
s = { sizeof init, init }; \
ELIM (expect == snprintf (0, 0, fmt, __VA_ARGS__)); \
} while (0)
/* Exercise a non-const local struct initialized by an initializer
list. */
void test_assign_aggregate (void)
{
T (0, "", "%s", s.a);
T (1, "1", "%s", s.a);
T (4, "1234", "%s", s.a);
T (5, "123", "s=%s", s.a);
T (5, "1234", "s=%s", s.a + 1);
T (2, "1234", "s=%s", s.a + 4);
T (5, "12345", "s=%s", &s.a[2]);
T (5, "123456", "s=%.*s", 3, &s.a[2]);
}
#undef T
#define T(expect, init, fmt, ...) \
do { \
char a[sizeof init]; \
strcpy (a, init); \
ELIM (expect == snprintf (0, 0, fmt, __VA_ARGS__)); \
} while (0)
/* Exercise a local char array initialized by a call to strcpy. */
void test_local_strcpy (void)
{
T (0, "", "%s", a);
T (1, "1", "%s", a);
T (2, "12", "%s", a);
T (3, "123", "%s", a);
T (4, "1234", "%s", a);
T (5, "123", "s=%s", a);
T (5, "1234", "s=%s", a + 1);
T (2, "1234", "s=%s", a + 4);
T (5, "12345", "s=%s", &a[2]);
T (5, "123456", "s=%.*s", 3, &a[2]);
}
#undef T
#define T(expect, init, fmt, ...) \
do { \
char a[n]; \
strcpy (a, init); \
ELIM (expect == snprintf (0, 0, fmt, __VA_ARGS__)); \
} while (0)
/* Exercise a VLA initialized by a call to strcpy. */
void test_vla_strcpy (unsigned n)
{
T (0, "", "%s", a);
T (1, "1", "%s", a);
T (2, "12", "%s", a);
T (3, "123", "%s", a);
T (4, "1234", "%s", a);
T (5, "123", "s=%s", a);
T (5, "1234", "s=%s", a + 1);
T (2, "1234", "s=%s", a + 4);
T (5, "12345", "s=%s", &a[2]);
T (5, "123456", "s=%.*s", 3, &a[2]);
}
/* { dg-final { scan-tree-dump-times "printf" 0 "optimized" } }
{ dg-final { scan-tree-dump-times "strlen" 0 "optimized" } }
{ dg-final { scan-tree-dump-times "not_eliminated" 0 "optimized" } } */
/* Test to verify that snprintf can determine the correct range
of lengths of dynamically constructed string arguments.
{ dg-do compile }
{ dg-options "-O2 -Wall -fdump-tree-optimized" } */
typedef __SIZE_TYPE__ size_t;
void* memcpy (void*, const void*, size_t);
char* strcpy (char * restrict, const char * restrict);
int snprintf (char * restrict, size_t, const char *restrict, ...);
void sink (void*, ...);
#define CONCAT(x, y) x ## y
#define CAT(x, y) CONCAT (x, y)
#define FAILNAME(name, counter) \
CAT (CAT (CAT (call_ ## name ##_on_line_, __LINE__), _), counter)
#define FAIL(name, counter) do { \
extern void FAILNAME (name, counter) (void); \
FAILNAME (name, counter)(); \
} while (0)
/* Macro to emit a call to funcation named
call_in_true_branch_not_eliminated_on_line_NNN()
for each call that's expected to be eliminated. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that no such call appears in output. */
#define VERIFY_ELIM(expr) \
if (!(expr)) FAIL (in_true_branch_not_eliminated, __COUNTER__); else (void)0
/* Macro to emit a call to a function named
call_made_in_{true,false}_branch_on_line_NNN()
for each call that's expected to be retained. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that the expected number of both kinds of calls appears in output
(a pair for each line with the invocation of the KEEP() macro. */
#define VERIFY_KEEP(expr) \
if (expr) \
FAIL (made_in_true_branch, __COUNTER__); \
else \
FAIL (made_in_false_branch, __COUNTER__)
#define ARGS(...) __VA_ARGS__
/* Each test macro expands to a new function to get around bug 81776
- missing sprintf optimization due to pointer escape analysis. */
#define ELIM(expect, dst, init, fmt, ...) \
void CAT (test_func_on_line_, __LINE__)(void) \
{ \
memcpy (dst, init, sizeof (init) - 1); \
const int res = snprintf (0, 0, fmt, __VA_ARGS__); \
VERIFY_ELIM (expect res); \
} typedef void dummy_typedef
#define KEEP(expect, dst, init, fmt, ...) \
void CAT (test_func_on_line_, __LINE__)(void) \
{ \
memcpy (dst, init, sizeof (init) - 1); \
const int ret = snprintf (0, 0, fmt, __VA_ARGS__); \
VERIFY_KEEP (expect ret); \
} typedef void dummy_typedef
/* Verify that conditions involving snprintf calls with a string
of some minimum but otherwise unbounded length stored in an array
of unknown bound are not folded unless the format string itself
restricts the maximum. The string could be longer than INT_MAX
making the snprintf call fail and return a negative value. */
extern char gax[];
KEEP (1 <=, gax, "1", "%s", gax);
KEEP (2 <=, gax, "12", "%s", gax);
KEEP (3 <=, gax, "123", "%s", gax);
ELIM (3 ==, gax, "123", "%.3s", gax);
ELIM (5 ==, gax, "123", "%.3s%.2s", gax, gax);
/* Disabled. The global pointer passed to memcpy as the destination
might point at itself, i.e., gptr == &gptr is a valid argument to
memcpy.
extern char *gptr;
KEEP (1 <=, gptr, "1", "%s", gptr);
KEEP (2 <=, gptr, "12", "%s", gptr);
KEEP (3 <=, gptr, "123", "%s", gptr);
ELIM (3 ==, gptr, "123", "%.3s", gptr);
ELIM (5 ==, gptr, "123", "%.3s%.2s", gptr, gptr);
*/
/* Verify that conditions involving snprintf calls with a string
of some minimum but otherwise unbounded length stored in an array
of a known bound are folded. The longest string that can be
stored in such arrays is bounded by the size of the array. */
extern char ga4[4];
ELIM (0 <=, ga4, "\0", "%s", ga4);
ELIM (3 >=, ga4, "\0", "%s", ga4);
ELIM (1 <=, ga4, "1", "%s", ga4);
ELIM (0 <=, ga4, "1", "%s", ga4 + 1);
ELIM (0 <=, ga4, "1", "%s", &ga4[1]);
ELIM (3 >=, ga4, "1", "%s", ga4);
ELIM (2 >=, ga4, "1", "%s", ga4 + 1);
ELIM (2 >=, ga4, "1", "%s", &ga4[1]);
ELIM (2 <=, ga4, "12", "%s", ga4);
ELIM (3 >=, ga4, "12", "%s", ga4);
ELIM (3 <=, ga4, "123", "%s", ga4);
ELIM (3 ==, ga4, "123", "%.3s", ga4);
ELIM (5 ==, ga4, "123", "%.3s%.2s", ga4, ga4);
/* Verify conditionals involving dynamically created strings of known
length stored in local arrays. */
#undef ELIM
#define ELIM(expect, N1, N2, init1, init2, fmt, ...) \
void CAT (test_func_on_line_, __LINE__)(int i) \
{ \
char a1[N1], a2[N2]; \
memcpy (a1, init1, sizeof (init1) - 1); \
memcpy (a2, init2, sizeof (init2) - 1); \
const int res = snprintf (0, 0, fmt, __VA_ARGS__); \
VERIFY_ELIM (expect res); \
} typedef void dummy_typedef
ELIM (0 ==, 2, 2, "\0", "\0", "%s", i ? a1 : a2);
ELIM (2 ==, 2, 2, "\0", "\0", "s=%s", i ? a1 : a2);
ELIM (1 ==, 2, 2, "a\0", "b\0", "%s", i ? a1 : a2);
ELIM (3 ==, 2, 2, "a\0", "b\0", "s=%s", i ? a1 : a2);
ELIM (2 ==, 3, 5, "ab\0", "cd\0", "%s", i ? a1 : a2);
ELIM (3 ==, 3, 5, "ab\0", "cd\0", "%3s", i ? a1 : a2);
ELIM (3 ==, 5, 5, "abcd\0", "efgh\0", "%.3s", i ? a1 : a2);
ELIM (3 ==, 4, 1, "abc\0", "", "%s", i ? a1 : "def");
ELIM (4 ==, 1, 5, "", "efgh\0", "%s", i ? "abcd" : a2);
ELIM (4 ==, 5, 5, "abcd\0", "efgh\0", "%s", i < 0 ? a1 : 0 < i ? a2 : "ijkl");
/* { dg-final { scan-tree-dump-times "_not_eliminated" 0 "optimized" } }
{ dg-final { scan-tree-dump-times "call_made_" 6 "optimized" } } */
/* Test to verify that snprintf can determine the correct range
of lengths of string arguments based on the results of prior
calls to strlen.
{ dg-do compile }
{ dg-options "-O2 -Wall -fdump-tree-optimized" } */
typedef __SIZE_TYPE__ size_t;
void abort (void);
size_t strlen (const char *);
int snprintf (char * restrict, size_t, const char *restrict, ...);
void one_str_exact (const char *str)
{
if (1 == strlen (str))
if (1 != snprintf (0, 0, "%s", str))
abort ();
}
void two_str_exact (const char *s1, const char *s2)
{
if (1 == strlen (s1) && 2 == strlen (s2))
if (3 != snprintf (0, 0, "%s%s", s1, s2))
abort ();
}
void one_str_maxlen (const char *str)
{
if (2 >= strlen (str))
if (2 < snprintf (0, 0, "%s", str))
abort ();
}
void two_str_maxlen (const char *s1, const char *s2)
{
if (2 >= strlen (s1) && 3 >= strlen (s2))
if (5 < snprintf (0, 0, "%s%s", s1, s2))
abort ();
}
/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */
/* Test to verify that --param ssa_name_def_chain_limit can be used to
limit the maximum number of SSA_NAME assignments the built-in code
follows to determine the variable value/string length.
{ dg-do compile }
{ dg-options "-O2 -Wall --param ssa-name-def-chain-limit=4 -fdump-tree-optimized" } */
void abort (void);
int sprintf (char * restrict, const char *restrict, ...);
void sink (const char*, ...);
const char a0[] = "";
const char a1[] = "1";
const char a2[] = "12";
const char a3[] = "123";
const char a4[] = "1234";
const char a5[] = "12345";
const char a6[] = "123456";
const char a7[] = "1234567";
const char a8[] = "12345678";
const char a9[] = "123456789";
int i0, i1, i2, i3, i4, i5, i6, i7, i8;
void g1 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
sink (p0, p1);
if (sprintf (d, "%s", p1) > 2)
abort ();
}
void g2 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
sink (p0, p1, p2);
if (sprintf (d, "%s", p2) > 3)
abort ();
}
void g3 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
sink (p0, p1, p2, p3);
if (sprintf (d, "%s", p3) > 4)
abort ();
}
void g4 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
const char *p4 = i4 ? p3 : a5;
sink (p0, p1, p2, p3, p4);
// p4 below is the result of the following five PHI assignments
// and with the limit set to 4 the sprintf call result is not
// determined:
// iftmp.0_7 = PHI <&a0(2), &a1(3)>
// iftmp.2_8 = PHI <iftmp.0_7(4), &a2(5)>
// iftmp.4_9 = PHI <iftmp.2_8(6), &a3(7)>
// iftmp.6_10 = PHI <iftmp.4_9(8), &a4(9)>
// iftmp.8_17 = PHI <iftmp.6_10(10), &a5(11)>
// p4 = iftmp.8_17
extern void keep_g4 (void);
if (sprintf (d, "%s", p4) > 5)
keep_g4 ();
}
void g5 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
const char *p4 = i4 ? p3 : a5;
const char *p5 = i5 ? p4 : a6;
sink (p0, p1, p2, p3, p4, p5);
extern void keep_g5 (void);
if (sprintf (d, "%s", p5) > 6)
keep_g5 ();
/* { dg-final { scan-tree-dump-times "keep_g5" 1 "optimized" } } */
}
void g6 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
const char *p4 = i4 ? p3 : a5;
const char *p5 = i5 ? p4 : a6;
const char *p6 = i6 ? p5 : a7;
sink (p0, p1, p2, p3, p4, p5, p6);
extern void keep_g6 (void);
if (sprintf (d, "%s", p6) > 7)
keep_g6 ();
/* { dg-final { scan-tree-dump-times "keep_g6" 1 "optimized" } } */
}
void g7 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
const char *p4 = i4 ? p3 : a5;
const char *p5 = i5 ? p4 : a6;
const char *p6 = i6 ? p5 : a7;
const char *p7 = i7 ? p6 : a8;
sink (p0, p1, p2, p3, p4, p5, p6, p7);
extern void keep_g7 (void);
if (sprintf (d, "%s", p7) > 8)
keep_g7 ();
/* { dg-final { scan-tree-dump-times "keep_g7" 1 "optimized" } } */
}
void g8 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
const char *p4 = i4 ? p3 : a5;
const char *p5 = i5 ? p4 : a6;
const char *p6 = i6 ? p5 : a7;
const char *p7 = i7 ? p6 : a8;
const char *p8 = i8 ? p7 : a9;
sink (p0, p1, p2, p3, p4, p5, p6, p7, p8);
extern void keep_g8 (void);
if (sprintf (d, "%s", p8) > 9)
keep_g8 ();
/* { dg-final { scan-tree-dump-times "keep_g8" 1 "optimized" } } */
}
/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */
/* Test to verify that --param ssa_name_def_chain_limit can be used to
limit the maximum number of SSA_NAME assignments the built-in code
follows.
{ dg-do compile }
{ dg-options "-O2 -Wall -Wformat-truncation=2 --param ssa-name-def-chain-limit=4 -fdump-tree-optimized" } */
typedef __SIZE_TYPE__ size_t;
int snprintf (char * restrict, size_t, const char *restrict, ...);
void sink (const char*, ...);
const char a0[] = "";
const char a1[] = "1";
const char a2[] = "12";
const char a3[] = "123";
const char a4[] = "1234";
const char a5[] = "12345";
const char a6[] = "123456";
const char a7[] = "1234567";
const char a8[] = "12345678";
const char a9[] = "123456789";
int i0, i1, i2, i3, i4, i5, i6, i7, i8;
void g1 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
sink (p0, p1);
snprintf (d, 1, "%s", p1); // { dg-warning "\\\[-Wformat-truncation" }
}
void g2 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
sink (p0, p1, p2);
snprintf (d, 2, "%s", p2); // { dg-warning "\\\[-Wformat-truncation" }
}
void g3 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
sink (p0, p1, p2, p3);
snprintf (d, 3, "%s", p3); // { dg-warning "\\\[-Wformat-truncation" }
}
void g4 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
const char *p4 = i4 ? p3 : a5;
sink (p0, p1, p2, p3, p4);
// p4 below is the result of the following five PHI assignments
// and with the limit set to 4 the snprintf call is not diagnosed
// iftmp.0_7 = PHI <&a0(2), &a1(3)>
// iftmp.2_8 = PHI <iftmp.0_7(4), &a2(5)>
// iftmp.4_9 = PHI <iftmp.2_8(6), &a3(7)>
// iftmp.6_10 = PHI <iftmp.4_9(8), &a4(9)>
// iftmp.8_17 = PHI <iftmp.6_10(10), &a5(11)>
// p4 = iftmp.8_17
snprintf (d, 4, "%s", p4);
}
void g5 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
const char *p4 = i4 ? p3 : a5;
const char *p5 = i5 ? p4 : a6;
sink (p0, p1, p2, p3, p4, p5);
snprintf (d, 5, "%s", p5);
}
void g6 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
const char *p4 = i4 ? p3 : a5;
const char *p5 = i5 ? p4 : a6;
const char *p6 = i6 ? p5 : a7;
sink (p0, p1, p2, p3, p4, p5, p6);
snprintf (d, 6, "%s", p6);
}
void g7 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
const char *p4 = i4 ? p3 : a5;
const char *p5 = i5 ? p4 : a6;
const char *p6 = i6 ? p5 : a7;
const char *p7 = i7 ? p6 : a8;
sink (p0, p1, p2, p3, p4, p5, p6, p7);
snprintf (d, 7, "%s", p7);
}
void g8 (char *d)
{
const char *p0 = i0 ? a0 : a1;
const char *p1 = i1 ? p0 : a2;
const char *p2 = i2 ? p1 : a3;
const char *p3 = i3 ? p2 : a4;
const char *p4 = i4 ? p3 : a5;
const char *p5 = i5 ? p4 : a6;
const char *p6 = i6 ? p5 : a7;
const char *p7 = i7 ? p6 : a8;
const char *p8 = i8 ? p7 : a9;
sink (p0, p1, p2, p3, p4, p5, p6, p7, p8);
snprintf (d, 8, "%s", p8);
}
/* PR tree-optimization/83431 -Wformat-truncation may incorrectly report
truncation
{ dg-do compile }
{ dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
typedef __SIZE_TYPE__ size_t;
extern int snprintf (char*, size_t, const char*, ...);
extern char* strcpy (char*, const char*);
struct S
{
char a9[9];
char a5[5];
int x;
};
void test_assign_nowarn (struct S* s)
{
int i = 0;
{
char a9[9] = "1234";
snprintf (s[i].a5, sizeof (s[i].a5), "%s", a9); /* { dg-bogus "\\\[-Wformat-truncation]" } */
}
{
++i;
char a8[8] = "123";
snprintf (s[i].a5, sizeof (s[i].a5), "%s\n", a8); /* { dg-bogus "\\\[-Wformat-truncation]" } */
}
{
++i;
char a7[7] = "12";
snprintf (s[i].a5, sizeof (s[i].a5), "[%s]", a7); /* { dg-bogus "\\\[-Wformat-truncation]" } */
}
{
++i;
char a6[6] = "1";
snprintf (s[i].a5, sizeof (s[i].a5), "[%s]\n", a6); /* { dg-bogus "\\\[-Wformat-truncation]" } */
}
}
void test_strcpy_nowarn (struct S* s)
{
int i = 0;
strcpy (s[i].a9, "1234");
snprintf (s[i].a5, sizeof (s[i].a5), "%s", s[i].a9);
++i;
strcpy (s[i].a9, "123");
snprintf (s[i].a5, sizeof (s[i].a5), "%s\n", s[i].a9); /* { dg-bogus "\\\[-Wformat-truncation]" } */
++i;
strcpy (s[i].a9, "12");
snprintf (s[i].a5, sizeof (s[i].a5), "[%s]", s[i].a9); /* { dg-bogus "\\\[-Wformat-truncation]" } */
++i;
strcpy (s[i].a9, "1");
snprintf (s[i].a5, sizeof (s[i].a5), "[%s]\n", s[i].a9); /* { dg-bogus "\\\[-Wformat-truncation]" } */
}
void test_warn (struct S* s)
{
int i = 0;
strcpy (s[i].a9, "12345678");
snprintf (s[i].a5, sizeof (s[i].a5), "%s", s[i].a9); /* { dg-warning "'%s' directive output truncated writing 8 bytes into a region of size 5" } */
++i;
strcpy (s[i].a9, "1234567");
snprintf (s[i].a5, sizeof (s[i].a5), "%s", s[i].a9); /* { dg-warning "'%s' directive output truncated writing 7 bytes into a region of size 5" } */
++i;
strcpy (s[i].a9, "123456");
snprintf (s[i].a5, sizeof (s[i].a5), "%s", s[i].a9); /* { dg-warning "'%s' directive output truncated writing 6 bytes into a region of size 5" } */
++i;
strcpy (s[i].a9, "12345");
snprintf (s[i].a5, sizeof (s[i].a5), "%s", s[i].a9); /* { dg-warning "'snprintf' output truncated before the last format character" } */
++i;
strcpy (s[i].a9, "1234");
snprintf (s[i].a5, sizeof (s[i].a5), "%s\n", s[i].a9); /* { dg-warning "output truncated before the last format character" } */
++i;
strcpy (s[i].a9, "123");
snprintf (s[i].a5, sizeof (s[i].a5), ">%s<", s[i].a9); /* { dg-warning "output truncated before the last format character" } */
}
/* PR middle-end/87052 - STRING_CST printing incomplete in Gimple dumps
{ dg-do compile }
{ dg-options "-fdump-tree-original" } */
void* f (char *d, int c)
{
return __builtin_memchr ("1\0\0", c, 4);
}
/* Veriy the full string appears in the dump:
{ dg-final { scan-tree-dump "\"1\\\\x00\\\\x00\"" "original" } } */
...@@ -11,4 +11,4 @@ void f (void) ...@@ -11,4 +11,4 @@ void f (void)
__builtin_abort (); __builtin_abort ();
} }
/* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen" } } */ /* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen1" } } */
...@@ -12,4 +12,4 @@ void f3 (void) ...@@ -12,4 +12,4 @@ void f3 (void)
f (__builtin_strlen (s)); f (__builtin_strlen (s));
} }
/* { dg-final { scan-tree-dump-times "strlen" 0 "strlen" } } */ /* { dg-final { scan-tree-dump-times "strlen" 0 "strlen1" } } */
...@@ -419,6 +419,7 @@ extern gimple_opt_pass *make_pass_omp_target_link (gcc::context *ctxt); ...@@ -419,6 +419,7 @@ extern gimple_opt_pass *make_pass_omp_target_link (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_oacc_device_lower (gcc::context *ctxt); extern gimple_opt_pass *make_pass_oacc_device_lower (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_omp_device_lower (gcc::context *ctxt); extern gimple_opt_pass *make_pass_omp_device_lower (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_object_sizes (gcc::context *ctxt); extern gimple_opt_pass *make_pass_object_sizes (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_warn_printf (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_strlen (gcc::context *ctxt); extern gimple_opt_pass *make_pass_strlen (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_fold_builtins (gcc::context *ctxt); extern gimple_opt_pass *make_pass_fold_builtins (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_post_ipa_warn (gcc::context *ctxt); extern gimple_opt_pass *make_pass_post_ipa_warn (gcc::context *ctxt);
......
...@@ -55,6 +55,12 @@ along with GCC; see the file COPYING3. If not see ...@@ -55,6 +55,12 @@ along with GCC; see the file COPYING3. If not see
#include "intl.h" #include "intl.h"
#include "attribs.h" #include "attribs.h"
#include "calls.h" #include "calls.h"
#include "cfgloop.h"
#include "tree-ssa-loop.h"
#include "tree-scalar-evolution.h"
#include "vr-values.h"
#include "gimple-ssa-evrp-analyze.h"
/* A vector indexed by SSA_NAME_VERSION. 0 means unknown, positive value /* A vector indexed by SSA_NAME_VERSION. 0 means unknown, positive value
is an index into strinfo vector, negative value stands for is an index into strinfo vector, negative value stands for
...@@ -64,6 +70,9 @@ static vec<int> ssa_ver_to_stridx; ...@@ -64,6 +70,9 @@ static vec<int> ssa_ver_to_stridx;
/* Number of currently active string indexes plus one. */ /* Number of currently active string indexes plus one. */
static int max_stridx; static int max_stridx;
/* Set to true to optimize, false when just checking. */
static bool strlen_optimize;
/* String information record. */ /* String information record. */
struct strinfo struct strinfo
{ {
...@@ -154,7 +163,8 @@ struct decl_stridxlist_map ...@@ -154,7 +163,8 @@ struct decl_stridxlist_map
/* Hash table for mapping decls to a chained list of offset -> idx /* Hash table for mapping decls to a chained list of offset -> idx
mappings. */ mappings. */
static hash_map<tree_decl_hash, stridxlist> *decl_to_stridxlist_htab; typedef hash_map<tree_decl_hash, stridxlist> decl_to_stridxlist_htab_t;
static decl_to_stridxlist_htab_t *decl_to_stridxlist_htab;
/* Hash table mapping strlen (or strnlen with constant bound and return /* Hash table mapping strlen (or strnlen with constant bound and return
smaller than bound) calls to stridx instances describing smaller than bound) calls to stridx instances describing
...@@ -604,14 +614,21 @@ set_endptr_and_length (location_t loc, strinfo *si, tree endptr) ...@@ -604,14 +614,21 @@ set_endptr_and_length (location_t loc, strinfo *si, tree endptr)
si->full_string_p = true; si->full_string_p = true;
} }
/* Return string length, or NULL if it can't be computed. */ /* Return the string length, or NULL if it can't be computed.
The length may but need not be constant. Instead, it might be
the result of a strlen() call. */
static tree static tree
get_string_length (strinfo *si) get_string_length (strinfo *si)
{ {
/* If the length has already been computed return it if it's exact
(i.e., the string is nul-terminated at NONZERO_CHARS), or return
null if it isn't. */
if (si->nonzero_chars) if (si->nonzero_chars)
return si->full_string_p ? si->nonzero_chars : NULL; return si->full_string_p ? si->nonzero_chars : NULL;
/* If the string is the result of one of the built-in calls below
attempt to compute the length from the call statement. */
if (si->stmt) if (si->stmt)
{ {
gimple *stmt = si->stmt, *lenstmt; gimple *stmt = si->stmt, *lenstmt;
...@@ -702,6 +719,336 @@ get_string_length (strinfo *si) ...@@ -702,6 +719,336 @@ get_string_length (strinfo *si)
return si->nonzero_chars; return si->nonzero_chars;
} }
/* Dump strlen data to FP for statement STMT. When non-null, RVALS
points to EVRP info and is used to dump strlen range for non-constant
results. */
DEBUG_FUNCTION void
dump_strlen_info (FILE *fp, gimple *stmt, const vr_values *rvals)
{
if (stmt)
{
fprintf (fp, "\nDumping strlen pass data after ");
print_gimple_expr (fp, stmt, TDF_LINENO);
fputc ('\n', fp);
}
else
fprintf (fp, "\nDumping strlen pass data\n");
fprintf (fp, "max_stridx = %i\n", max_stridx);
fprintf (fp, "ssa_ver_to_stridx has %u elements\n",
ssa_ver_to_stridx.length ());
fprintf (fp, "stridx_to_strinfo");
if (stridx_to_strinfo)
{
fprintf (fp, " has %u elements\n", stridx_to_strinfo->length ());
for (unsigned i = 0; i != stridx_to_strinfo->length (); ++i)
{
if (strinfo *si = (*stridx_to_strinfo)[i])
{
if (!si->idx)
continue;
fprintf (fp, " idx = %i", si->idx);
if (si->ptr)
{
fprintf (fp, ", ptr = ");
print_generic_expr (fp, si->ptr);
}
fprintf (fp, ", nonzero_chars = ");
print_generic_expr (fp, si->nonzero_chars);
if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
{
value_range_kind rng = VR_UNDEFINED;
wide_int min, max;
if (rvals)
{
const value_range *vr
= CONST_CAST (class vr_values *, rvals)
->get_value_range (si->nonzero_chars);
rng = vr->kind ();
if (range_int_cst_p (vr))
{
min = wi::to_wide (vr->min ());
max = wi::to_wide (vr->max ());
}
else
rng = VR_UNDEFINED;
}
else
rng = get_range_info (si->nonzero_chars, &min, &max);
if (rng == VR_RANGE || rng == VR_ANTI_RANGE)
{
fprintf (fp, " %s[%llu, %llu]",
rng == VR_RANGE ? "" : "~",
(long long) min.to_uhwi (),
(long long) max.to_uhwi ());
}
}
fprintf (fp, " , refcount = %i", si->refcount);
if (si->stmt)
{
fprintf (fp, ", stmt = ");
print_gimple_expr (fp, si->stmt, 0);
}
if (si->writable)
fprintf (fp, ", writable");
if (si->full_string_p)
fprintf (fp, ", full_string_p");
if (strinfo *next = get_next_strinfo (si))
{
fprintf (fp, ", {");
do
fprintf (fp, "%i%s", next->idx, next->first ? ", " : "");
while ((next = get_next_strinfo (next)));
fprintf (fp, "}");
}
fputs ("\n", fp);
}
}
}
else
fprintf (fp, " = null\n");
fprintf (fp, "decl_to_stridxlist_htab");
if (decl_to_stridxlist_htab)
{
fputs ("\n", fp);
typedef decl_to_stridxlist_htab_t::iterator iter_t;
for (iter_t it = decl_to_stridxlist_htab->begin ();
it != decl_to_stridxlist_htab->end (); ++it)
{
tree decl = (*it).first;
stridxlist *list = &(*it).second;
fprintf (fp, " decl = ");
print_generic_expr (fp, decl);
if (list)
{
fprintf (fp, ", offsets = {");
for (; list; list = list->next)
fprintf (fp, "%lli%s", (long long) list->offset,
list->next ? ", " : "");
fputs ("}", fp);
}
fputs ("\n", fp);
}
}
else
fprintf (fp, " = null\n");
if (laststmt.stmt)
{
fprintf (fp, "laststmt = ");
print_gimple_expr (fp, laststmt.stmt, 0);
fprintf (fp, ", len = ");
print_generic_expr (fp, laststmt.len);
fprintf (fp, ", stridx = %i\n", laststmt.stridx);
}
}
/* Attempt to determine the length of the string SRC. On success, store
the length in *PDATA and return true. Otherwise, return false.
VISITED is a bitmap of visited PHI nodes. RVALS points to EVRP info
and PSSA_DEF_MAX to an SSA_NAME assignment limit used to prevent runaway
recursion. */
static bool
get_range_strlen_dynamic (tree src, c_strlen_data *pdata, bitmap *visited,
const vr_values *rvals, unsigned *pssa_def_max)
{
int idx = get_stridx (src);
if (!idx)
{
if (TREE_CODE (src) == SSA_NAME)
{
gimple *def_stmt = SSA_NAME_DEF_STMT (src);
if (gimple_code (def_stmt) == GIMPLE_PHI)
{
if (!*visited)
*visited = BITMAP_ALLOC (NULL);
if (!bitmap_set_bit (*visited, SSA_NAME_VERSION (src)))
return true;
if (*pssa_def_max == 0)
return false;
--*pssa_def_max;
/* Iterate over the PHI arguments and determine the minimum
and maximum length/size of each and incorporate them into
the overall result. */
gphi *phi = as_a <gphi *> (def_stmt);
for (unsigned i = 0; i != gimple_phi_num_args (phi); ++i)
{
tree arg = gimple_phi_arg_def (phi, i);
if (arg == gimple_phi_result (def_stmt))
continue;
c_strlen_data argdata = { };
if (get_range_strlen_dynamic (arg, &argdata, visited, rvals,
pssa_def_max))
{
/* Set the DECL of an unterminated array this argument
refers to if one hasn't been found yet. */
if (!pdata->decl && argdata.decl)
pdata->decl = argdata.decl;
if (!argdata.minlen
|| (integer_zerop (argdata.minlen)
&& integer_all_onesp (argdata.maxbound)
&& integer_all_onesp (argdata.maxlen)))
{
/* Set the upper bound of the length to unbounded. */
pdata->maxlen = build_all_ones_cst (size_type_node);
continue;
}
/* Adjust the minimum and maximum length determined
so far and the upper bound on the array size. */
if (!pdata->minlen
|| tree_int_cst_lt (argdata.minlen, pdata->minlen))
pdata->minlen = argdata.minlen;
if (!pdata->maxlen
|| tree_int_cst_lt (pdata->maxlen, argdata.maxlen))
pdata->maxlen = argdata.maxlen;
if (!pdata->maxbound
|| (tree_int_cst_lt (pdata->maxbound,
argdata.maxbound)
&& !integer_all_onesp (argdata.maxbound)))
pdata->maxbound = argdata.maxbound;
}
else
pdata->maxlen = build_all_ones_cst (size_type_node);
}
return true;
}
}
/* Return success regardless of the result and handle *PDATA
in the caller. */
get_range_strlen (src, pdata, 1);
return true;
}
if (idx < 0)
{
/* SRC is a string of constant length. */
pdata->minlen = build_int_cst (size_type_node, ~idx);
pdata->maxlen = pdata->minlen;
pdata->maxbound = pdata->maxlen;
return true;
}
if (strinfo *si = get_strinfo (idx))
{
pdata->minlen = get_string_length (si);
if (!pdata->minlen
&& si->nonzero_chars)
{
if (TREE_CODE (si->nonzero_chars) == INTEGER_CST)
pdata->minlen = si->nonzero_chars;
else if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
{
const value_range *vr
= CONST_CAST (class vr_values *, rvals)
->get_value_range (si->nonzero_chars);
if (vr->kind () == VR_RANGE
&& range_int_cst_p (vr))
{
pdata->minlen = vr->min ();
pdata->maxlen = vr->max ();
}
else
pdata->minlen = build_zero_cst (size_type_node);
}
else
pdata->minlen = build_zero_cst (size_type_node);
tree base = si->ptr;
if (TREE_CODE (base) == ADDR_EXPR)
base = TREE_OPERAND (base, 0);
HOST_WIDE_INT off;
poly_int64 poff;
base = get_addr_base_and_unit_offset (base, &poff);
if (base
&& DECL_P (base)
&& TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE
&& TYPE_SIZE_UNIT (TREE_TYPE (base))
&& poff.is_constant (&off))
{
tree basetype = TREE_TYPE (base);
tree size = TYPE_SIZE_UNIT (basetype);
++off; /* Increment for the terminating nul. */
pdata->maxlen = fold_build2 (MINUS_EXPR, size_type_node, size,
build_int_cst (size_type_node, off));
pdata->maxbound = pdata->maxlen;
}
else
pdata->maxlen = build_all_ones_cst (size_type_node);
}
else if (TREE_CODE (pdata->minlen) == SSA_NAME)
{
const value_range *vr
= CONST_CAST (class vr_values *, rvals)
->get_value_range (si->nonzero_chars);
if (vr->kind () == VR_RANGE
&& range_int_cst_p (vr))
{
pdata->minlen = vr->min ();
pdata->maxlen = vr->max ();
pdata->maxbound = pdata->maxlen;
}
else
{
pdata->minlen = build_zero_cst (size_type_node);
pdata->maxlen = build_all_ones_cst (size_type_node);
}
}
else
{
pdata->maxlen = pdata->minlen;
pdata->maxbound = pdata->minlen;
}
return true;
}
return false;
}
/* Analogous to get_range_strlen but for dynamically created strings,
i.e., those created by calls to strcpy as opposed to just string
constants.
Try to obtain the range of the lengths of the string(s) referenced
by SRC, or the size of the largest array SRC refers to if the range
of lengths cannot be determined, and store all in *PDATA. RVALS
points to EVRP info. */
void
get_range_strlen_dynamic (tree src, c_strlen_data *pdata,
const vr_values *rvals)
{
bitmap visited = NULL;
unsigned limit = PARAM_VALUE (PARAM_SSA_NAME_DEF_CHAIN_LIMIT);
if (!get_range_strlen_dynamic (src, pdata, &visited, rvals, &limit))
{
/* On failure extend the length range to an impossible maximum
(a valid MAXLEN must be less than PTRDIFF_MAX - 1). Other
members can stay unchanged regardless. */
pdata->minlen = ssize_int (0);
pdata->maxlen = build_all_ones_cst (size_type_node);
}
else if (!pdata->minlen)
pdata->minlen = ssize_int (0);
if (visited)
BITMAP_FREE (visited);
}
/* Invalidate string length information for strings whose length /* Invalidate string length information for strings whose length
might change due to stores in stmt. */ might change due to stores in stmt. */
...@@ -4017,84 +4364,232 @@ is_char_type (tree type) ...@@ -4017,84 +4364,232 @@ is_char_type (tree type)
&& TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node)); && TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node));
} }
/* Check the built-in call at GSI for validity and optimize it.
Return true to let the caller advance *GSI to the statement
in the CFG and false otherwise. */
static bool
strlen_check_and_optimize_call (gimple_stmt_iterator *gsi,
const vr_values *rvals)
{
gimple *stmt = gsi_stmt (*gsi);
if (!flag_optimize_strlen
|| !strlen_optimize
|| !valid_builtin_call (stmt))
{
/* When not optimizing we must be checking printf calls which
we do even for user-defined functions when they are declared
with attribute format. */
handle_printf_call (gsi, rvals);
return true;
}
tree callee = gimple_call_fndecl (stmt);
switch (DECL_FUNCTION_CODE (callee))
{
case BUILT_IN_STRLEN:
case BUILT_IN_STRNLEN:
handle_builtin_strlen (gsi);
break;
case BUILT_IN_STRCHR:
handle_builtin_strchr (gsi);
break;
case BUILT_IN_STRCPY:
case BUILT_IN_STRCPY_CHK:
case BUILT_IN_STPCPY:
case BUILT_IN_STPCPY_CHK:
handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_STRNCAT:
case BUILT_IN_STRNCAT_CHK:
handle_builtin_strncat (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_STPNCPY:
case BUILT_IN_STPNCPY_CHK:
case BUILT_IN_STRNCPY:
case BUILT_IN_STRNCPY_CHK:
handle_builtin_stxncpy (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_MEMCPY:
case BUILT_IN_MEMCPY_CHK:
case BUILT_IN_MEMPCPY:
case BUILT_IN_MEMPCPY_CHK:
handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_STRCAT:
case BUILT_IN_STRCAT_CHK:
handle_builtin_strcat (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_MALLOC:
case BUILT_IN_CALLOC:
handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_MEMSET:
if (handle_builtin_memset (gsi))
return false;
break;
case BUILT_IN_MEMCMP:
if (handle_builtin_memcmp (gsi))
return false;
break;
case BUILT_IN_STRCMP:
case BUILT_IN_STRNCMP:
if (handle_builtin_string_cmp (gsi))
return false;
break;
default:
handle_printf_call (gsi, rvals);
break;
}
return true;
}
/* Handle an assignment statement at *GSI to a LHS of integral type.
If GSI's basic block needs clean-up of EH, set *CLEANUP_EH to true. */
static void
handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh)
{
gimple *stmt = gsi_stmt (*gsi);
tree lhs = gimple_assign_lhs (stmt);
tree lhs_type = TREE_TYPE (lhs);
enum tree_code code = gimple_assign_rhs_code (stmt);
if (code == COND_EXPR)
{
tree cond = gimple_assign_rhs1 (stmt);
enum tree_code cond_code = TREE_CODE (cond);
if (cond_code == EQ_EXPR || cond_code == NE_EXPR)
fold_strstr_to_strncmp (TREE_OPERAND (cond, 0),
TREE_OPERAND (cond, 1), stmt);
}
else if (code == EQ_EXPR || code == NE_EXPR)
fold_strstr_to_strncmp (gimple_assign_rhs1 (stmt),
gimple_assign_rhs2 (stmt), stmt);
else if (gimple_assign_load_p (stmt)
&& TREE_CODE (lhs_type) == INTEGER_TYPE
&& TYPE_MODE (lhs_type) == TYPE_MODE (char_type_node)
&& (TYPE_PRECISION (lhs_type)
== TYPE_PRECISION (char_type_node))
&& !gimple_has_volatile_ops (stmt))
{
tree off = integer_zero_node;
unsigned HOST_WIDE_INT coff = 0;
int idx = 0;
tree rhs1 = gimple_assign_rhs1 (stmt);
if (code == MEM_REF)
{
idx = get_stridx (TREE_OPERAND (rhs1, 0));
if (idx > 0)
{
strinfo *si = get_strinfo (idx);
if (si
&& si->nonzero_chars
&& TREE_CODE (si->nonzero_chars) == INTEGER_CST
&& (wi::to_widest (si->nonzero_chars)
>= wi::to_widest (off)))
off = TREE_OPERAND (rhs1, 1);
else
/* This case is not useful. See if get_addr_stridx
returns something usable. */
idx = 0;
}
}
if (idx <= 0)
idx = get_addr_stridx (rhs1, NULL_TREE, &coff);
if (idx > 0)
{
strinfo *si = get_strinfo (idx);
if (si
&& si->nonzero_chars
&& TREE_CODE (si->nonzero_chars) == INTEGER_CST)
{
widest_int w1 = wi::to_widest (si->nonzero_chars);
widest_int w2 = wi::to_widest (off) + coff;
if (w1 == w2
&& si->full_string_p)
{
if (dump_file && (dump_flags & TDF_DETAILS) != 0)
{
fprintf (dump_file, "Optimizing: ");
print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
}
/* Reading the final '\0' character. */
tree zero = build_int_cst (lhs_type, 0);
gimple_set_vuse (stmt, NULL_TREE);
gimple_assign_set_rhs_from_tree (gsi, zero);
*cleanup_eh
|= maybe_clean_or_replace_eh_stmt (stmt,
gsi_stmt (*gsi));
stmt = gsi_stmt (*gsi);
update_stmt (stmt);
if (dump_file && (dump_flags & TDF_DETAILS) != 0)
{
fprintf (dump_file, "into: ");
print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
}
}
else if (w1 > w2)
{
/* Reading a character before the final '\0'
character. Just set the value range to ~[0, 0]
if we don't have anything better. */
wide_int min, max;
signop sign = TYPE_SIGN (lhs_type);
int prec = TYPE_PRECISION (lhs_type);
value_range_kind vr = get_range_info (lhs, &min, &max);
if (vr == VR_VARYING
|| (vr == VR_RANGE
&& min == wi::min_value (prec, sign)
&& max == wi::max_value (prec, sign)))
set_range_info (lhs, VR_ANTI_RANGE,
wi::zero (prec), wi::zero (prec));
}
}
}
}
if (strlen_to_stridx)
{
tree rhs1 = gimple_assign_rhs1 (stmt);
if (stridx_strlenloc *ps = strlen_to_stridx->get (rhs1))
strlen_to_stridx->put (lhs, stridx_strlenloc (*ps));
}
}
/* Attempt to check for validity of the performed access a single statement /* Attempt to check for validity of the performed access a single statement
at *GSI using string length knowledge, and to optimize it. at *GSI using string length knowledge, and to optimize it.
If the given basic block needs clean-up of EH, CLEANUP_EH is set to If the given basic block needs clean-up of EH, CLEANUP_EH is set to
true. */ true. */
static bool static bool
strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh) check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
const vr_values *rvals)
{ {
gimple *stmt = gsi_stmt (*gsi); gimple *stmt = gsi_stmt (*gsi);
if (is_gimple_call (stmt)) if (is_gimple_call (stmt))
{ {
tree callee = gimple_call_fndecl (stmt); if (!strlen_check_and_optimize_call (gsi, rvals))
if (valid_builtin_call (stmt)) return false;
switch (DECL_FUNCTION_CODE (callee))
{
case BUILT_IN_STRLEN:
case BUILT_IN_STRNLEN:
handle_builtin_strlen (gsi);
break;
case BUILT_IN_STRCHR:
handle_builtin_strchr (gsi);
break;
case BUILT_IN_STRCPY:
case BUILT_IN_STRCPY_CHK:
case BUILT_IN_STPCPY:
case BUILT_IN_STPCPY_CHK:
handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_STRNCAT:
case BUILT_IN_STRNCAT_CHK:
handle_builtin_strncat (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_STPNCPY:
case BUILT_IN_STPNCPY_CHK:
case BUILT_IN_STRNCPY:
case BUILT_IN_STRNCPY_CHK:
handle_builtin_stxncpy (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_MEMCPY:
case BUILT_IN_MEMCPY_CHK:
case BUILT_IN_MEMPCPY:
case BUILT_IN_MEMPCPY_CHK:
handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_STRCAT:
case BUILT_IN_STRCAT_CHK:
handle_builtin_strcat (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_MALLOC:
case BUILT_IN_CALLOC:
handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi);
break;
case BUILT_IN_MEMSET:
if (handle_builtin_memset (gsi))
return false;
break;
case BUILT_IN_MEMCMP:
if (handle_builtin_memcmp (gsi))
return false;
break;
case BUILT_IN_STRCMP:
case BUILT_IN_STRNCMP:
if (handle_builtin_string_cmp (gsi))
return false;
break;
default:
break;
}
} }
else if (!flag_optimize_strlen || !strlen_optimize)
return true;
else if (is_gimple_assign (stmt) && !gimple_clobber_p (stmt)) else if (is_gimple_assign (stmt) && !gimple_clobber_p (stmt))
{ {
/* Handle non-clobbering assignment. */
tree lhs = gimple_assign_lhs (stmt); tree lhs = gimple_assign_lhs (stmt);
tree lhs_type = TREE_TYPE (lhs);
if (TREE_CODE (lhs) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (lhs))) if (TREE_CODE (lhs) == SSA_NAME && POINTER_TYPE_P (lhs_type))
{ {
if (gimple_assign_single_p (stmt) if (gimple_assign_single_p (stmt)
|| (gimple_assign_cast_p (stmt) || (gimple_assign_cast_p (stmt)
...@@ -4106,117 +4601,10 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh) ...@@ -4106,117 +4601,10 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh)
else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
handle_pointer_plus (gsi); handle_pointer_plus (gsi);
} }
else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (lhs))) else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (lhs_type))
{ /* Handle assignment to a character. */
enum tree_code code = gimple_assign_rhs_code (stmt); handle_integral_assign (gsi, cleanup_eh);
if (code == COND_EXPR) else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
{
tree cond = gimple_assign_rhs1 (stmt);
enum tree_code cond_code = TREE_CODE (cond);
if (cond_code == EQ_EXPR || cond_code == NE_EXPR)
fold_strstr_to_strncmp (TREE_OPERAND (cond, 0),
TREE_OPERAND (cond, 1), stmt);
}
else if (code == EQ_EXPR || code == NE_EXPR)
fold_strstr_to_strncmp (gimple_assign_rhs1 (stmt),
gimple_assign_rhs2 (stmt), stmt);
else if (gimple_assign_load_p (stmt)
&& TREE_CODE (TREE_TYPE (lhs)) == INTEGER_TYPE
&& TYPE_MODE (TREE_TYPE (lhs)) == TYPE_MODE (char_type_node)
&& (TYPE_PRECISION (TREE_TYPE (lhs))
== TYPE_PRECISION (char_type_node))
&& !gimple_has_volatile_ops (stmt))
{
tree off = integer_zero_node;
unsigned HOST_WIDE_INT coff = 0;
int idx = 0;
tree rhs1 = gimple_assign_rhs1 (stmt);
if (code == MEM_REF)
{
idx = get_stridx (TREE_OPERAND (rhs1, 0));
if (idx > 0)
{
strinfo *si = get_strinfo (idx);
if (si
&& si->nonzero_chars
&& TREE_CODE (si->nonzero_chars) == INTEGER_CST
&& (wi::to_widest (si->nonzero_chars)
>= wi::to_widest (off)))
off = TREE_OPERAND (rhs1, 1);
else
/* This case is not useful. See if get_addr_stridx
returns something usable. */
idx = 0;
}
}
if (idx <= 0)
idx = get_addr_stridx (rhs1, NULL_TREE, &coff);
if (idx > 0)
{
strinfo *si = get_strinfo (idx);
if (si
&& si->nonzero_chars
&& TREE_CODE (si->nonzero_chars) == INTEGER_CST)
{
widest_int w1 = wi::to_widest (si->nonzero_chars);
widest_int w2 = wi::to_widest (off) + coff;
if (w1 == w2
&& si->full_string_p)
{
if (dump_file && (dump_flags & TDF_DETAILS) != 0)
{
fprintf (dump_file, "Optimizing: ");
print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
}
/* Reading the final '\0' character. */
tree zero = build_int_cst (TREE_TYPE (lhs), 0);
gimple_set_vuse (stmt, NULL_TREE);
gimple_assign_set_rhs_from_tree (gsi, zero);
*cleanup_eh
|= maybe_clean_or_replace_eh_stmt (stmt,
gsi_stmt (*gsi));
stmt = gsi_stmt (*gsi);
update_stmt (stmt);
if (dump_file && (dump_flags & TDF_DETAILS) != 0)
{
fprintf (dump_file, "into: ");
print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
}
}
else if (w1 > w2)
{
/* Reading a character before the final '\0'
character. Just set the value range to ~[0, 0]
if we don't have anything better. */
wide_int min, max;
tree type = TREE_TYPE (lhs);
enum value_range_kind vr
= get_range_info (lhs, &min, &max);
if (vr == VR_VARYING
|| (vr == VR_RANGE
&& min == wi::min_value (TYPE_PRECISION (type),
TYPE_SIGN (type))
&& max == wi::max_value (TYPE_PRECISION (type),
TYPE_SIGN (type))))
set_range_info (lhs, VR_ANTI_RANGE,
wi::zero (TYPE_PRECISION (type)),
wi::zero (TYPE_PRECISION (type)));
}
}
}
}
if (strlen_to_stridx)
{
tree rhs1 = gimple_assign_rhs1 (stmt);
if (stridx_strlenloc *ps = strlen_to_stridx->get (rhs1))
strlen_to_stridx->put (lhs, stridx_strlenloc (*ps));
}
}
else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
{ {
tree type = TREE_TYPE (lhs); tree type = TREE_TYPE (lhs);
if (TREE_CODE (type) == ARRAY_TYPE) if (TREE_CODE (type) == ARRAY_TYPE)
...@@ -4312,12 +4700,18 @@ class strlen_dom_walker : public dom_walker ...@@ -4312,12 +4700,18 @@ class strlen_dom_walker : public dom_walker
{ {
public: public:
strlen_dom_walker (cdi_direction direction) strlen_dom_walker (cdi_direction direction)
: dom_walker (direction), m_cleanup_cfg (false) : dom_walker (direction),
evrp (false),
m_cleanup_cfg (false)
{} {}
virtual edge before_dom_children (basic_block); virtual edge before_dom_children (basic_block);
virtual void after_dom_children (basic_block); virtual void after_dom_children (basic_block);
/* EVRP analyzer used for printf argument range processing, and
to track strlen results across integer variable assignments. */
evrp_range_analyzer evrp;
/* Flag that will trigger TODO_cleanup_cfg to be returned in strlen /* Flag that will trigger TODO_cleanup_cfg to be returned in strlen
execute function. */ execute function. */
bool m_cleanup_cfg; bool m_cleanup_cfg;
...@@ -4329,6 +4723,8 @@ public: ...@@ -4329,6 +4723,8 @@ public:
edge edge
strlen_dom_walker::before_dom_children (basic_block bb) strlen_dom_walker::before_dom_children (basic_block bb)
{ {
evrp.enter (bb);
basic_block dombb = get_immediate_dominator (CDI_DOMINATORS, bb); basic_block dombb = get_immediate_dominator (CDI_DOMINATORS, bb);
if (dombb == NULL) if (dombb == NULL)
...@@ -4402,8 +4798,16 @@ strlen_dom_walker::before_dom_children (basic_block bb) ...@@ -4402,8 +4798,16 @@ strlen_dom_walker::before_dom_children (basic_block bb)
/* Attempt to optimize individual statements. */ /* Attempt to optimize individual statements. */
for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); ) for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
if (strlen_check_and_optimize_stmt (&gsi, &cleanup_eh)) {
gsi_next (&gsi); gimple *stmt = gsi_stmt (gsi);
/* First record ranges generated by this statement so they
can be used by printf argument processing. */
evrp.record_ranges_from_stmt (stmt, false);
if (check_and_optimize_stmt (&gsi, &cleanup_eh, evrp.get_vr_values ()))
gsi_next (&gsi);
}
if (cleanup_eh && gimple_purge_dead_eh_edges (bb)) if (cleanup_eh && gimple_purge_dead_eh_edges (bb))
m_cleanup_cfg = true; m_cleanup_cfg = true;
...@@ -4420,6 +4824,8 @@ strlen_dom_walker::before_dom_children (basic_block bb) ...@@ -4420,6 +4824,8 @@ strlen_dom_walker::before_dom_children (basic_block bb)
void void
strlen_dom_walker::after_dom_children (basic_block bb) strlen_dom_walker::after_dom_children (basic_block bb)
{ {
evrp.leave (bb);
if (bb->aux) if (bb->aux)
{ {
stridx_to_strinfo = ((vec<strinfo *, va_heap, vl_embed> *) bb->aux); stridx_to_strinfo = ((vec<strinfo *, va_heap, vl_embed> *) bb->aux);
...@@ -4437,39 +4843,13 @@ strlen_dom_walker::after_dom_children (basic_block bb) ...@@ -4437,39 +4843,13 @@ strlen_dom_walker::after_dom_children (basic_block bb)
} }
} }
/* Main entry point. */
namespace { namespace {
const pass_data pass_data_strlen = static unsigned int
{ printf_strlen_execute (function *fun, bool warn_only)
GIMPLE_PASS, /* type */
"strlen", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_TREE_STRLEN, /* tv_id */
( PROP_cfg | PROP_ssa ), /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
class pass_strlen : public gimple_opt_pass
{ {
public: strlen_optimize = !warn_only;
pass_strlen (gcc::context *ctxt)
: gimple_opt_pass (pass_data_strlen, ctxt)
{}
/* opt_pass methods: */
virtual bool gate (function *) { return flag_optimize_strlen != 0; }
virtual unsigned int execute (function *);
}; // class pass_strlen
unsigned int
pass_strlen::execute (function *fun)
{
gcc_assert (!strlen_to_stridx); gcc_assert (!strlen_to_stridx);
if (warn_stringop_overflow || warn_stringop_truncation) if (warn_stringop_overflow || warn_stringop_truncation)
strlen_to_stridx = new hash_map<tree, stridx_strlenloc> (); strlen_to_stridx = new hash_map<tree, stridx_strlenloc> ();
...@@ -4479,10 +4859,17 @@ pass_strlen::execute (function *fun) ...@@ -4479,10 +4859,17 @@ pass_strlen::execute (function *fun)
calculate_dominance_info (CDI_DOMINATORS); calculate_dominance_info (CDI_DOMINATORS);
bool use_scev = optimize > 0 && flag_printf_return_value;
if (use_scev)
{
loop_optimizer_init (LOOPS_NORMAL);
scev_initialize ();
}
/* String length optimization is implemented as a walk of the dominator /* String length optimization is implemented as a walk of the dominator
tree and a forward walk of statements within each block. */ tree and a forward walk of statements within each block. */
strlen_dom_walker walker (CDI_DOMINATORS); strlen_dom_walker walker (CDI_DOMINATORS);
walker.walk (fun->cfg->x_entry_block_ptr); walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
ssa_ver_to_stridx.release (); ssa_ver_to_stridx.release ();
strinfo_pool.release (); strinfo_pool.release ();
...@@ -4503,12 +4890,114 @@ pass_strlen::execute (function *fun) ...@@ -4503,12 +4890,114 @@ pass_strlen::execute (function *fun)
strlen_to_stridx = NULL; strlen_to_stridx = NULL;
} }
if (use_scev)
{
scev_finalize ();
loop_optimizer_finalize ();
}
/* Clean up object size info. */
fini_object_sizes ();
return walker.m_cleanup_cfg ? TODO_cleanup_cfg : 0; return walker.m_cleanup_cfg ? TODO_cleanup_cfg : 0;
} }
/* This file defines two passes: one for warnings that runs only when
optimization is disabled, and another that implements optimizations
and also issues warnings. */
const pass_data pass_data_warn_printf =
{
GIMPLE_PASS, /* type */
"warn-printf", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_NONE, /* tv_id */
/* Normally an optimization pass would require PROP_ssa but because
this pass runs early, with no optimization, to do sprintf format
checking, it only requires PROP_cfg. */
PROP_cfg, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
class pass_warn_printf : public gimple_opt_pass
{
public:
pass_warn_printf (gcc::context *ctxt)
: gimple_opt_pass (pass_data_warn_printf, ctxt)
{}
virtual bool gate (function *);
virtual unsigned int execute (function *fun)
{
return printf_strlen_execute (fun, true);
}
};
/* Return true to run the warning pass only when not optimizing and
iff either -Wformat-overflow or -Wformat-truncation is specified. */
bool
pass_warn_printf::gate (function *)
{
return !optimize && (warn_format_overflow > 0 || warn_format_trunc > 0);
}
const pass_data pass_data_strlen =
{
GIMPLE_PASS, /* type */
"strlen", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_TREE_STRLEN, /* tv_id */
PROP_cfg | PROP_ssa, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
class pass_strlen : public gimple_opt_pass
{
public:
pass_strlen (gcc::context *ctxt)
: gimple_opt_pass (pass_data_strlen, ctxt)
{}
opt_pass * clone () { return new pass_strlen (m_ctxt); }
virtual bool gate (function *);
virtual unsigned int execute (function *fun)
{
return printf_strlen_execute (fun, false);
}
};
/* Return true to run the pass only when the sprintf and/or strlen
optimizations are enabled and -Wformat-overflow or -Wformat-truncation
are specified. */
bool
pass_strlen::gate (function *)
{
return ((warn_format_overflow > 0
|| warn_format_trunc > 0
|| flag_optimize_strlen > 0
|| flag_printf_return_value)
&& optimize > 0);
}
} // anon namespace } // anon namespace
gimple_opt_pass * gimple_opt_pass *
make_pass_warn_printf (gcc::context *ctxt)
{
return new pass_warn_printf (ctxt);
}
gimple_opt_pass *
make_pass_strlen (gcc::context *ctxt) make_pass_strlen (gcc::context *ctxt)
{ {
return new pass_strlen (ctxt); return new pass_strlen (ctxt);
......
...@@ -25,4 +25,11 @@ extern bool is_strlen_related_p (tree, tree); ...@@ -25,4 +25,11 @@ extern bool is_strlen_related_p (tree, tree);
extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree); extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
extern tree set_strlen_range (tree, wide_int, wide_int, tree = NULL_TREE); extern tree set_strlen_range (tree, wide_int, wide_int, tree = NULL_TREE);
struct c_strlen_data;
class vr_values;
extern void get_range_strlen_dynamic (tree , c_strlen_data *, const vr_values *);
/* APIs internal to strlen pass. Defined in in gimple-ssa-sprintf.c. */
extern bool handle_printf_call (gimple_stmt_iterator *, const vr_values *);
#endif // GCC_TREE_SSA_STRLEN_H #endif // GCC_TREE_SSA_STRLEN_H
...@@ -369,7 +369,7 @@ value_range_base::singleton_p (tree *result) const ...@@ -369,7 +369,7 @@ value_range_base::singleton_p (tree *result) const
tree tree
value_range_base::type () const value_range_base::type () const
{ {
gcc_assert (m_min || undefined_p ()); gcc_assert (m_min);
return TREE_TYPE (min ()); return TREE_TYPE (min ());
} }
......
...@@ -234,7 +234,7 @@ vr_values::update_value_range (const_tree var, value_range *new_vr) ...@@ -234,7 +234,7 @@ vr_values::update_value_range (const_tree var, value_range *new_vr)
called, if we are anyway, keep it VARYING. */ called, if we are anyway, keep it VARYING. */
if (old_vr->varying_p ()) if (old_vr->varying_p ())
{ {
new_vr->set_varying (new_vr->type ()); new_vr->set_varying (TREE_TYPE (var));
is_new = false; is_new = false;
} }
else if (new_vr->undefined_p ()) else if (new_vr->undefined_p ())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment