+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>
* config/i386/i386-features.c (general_remove_non_convertible_regs):
#include "domwalk.h"
#include "alloc-pool.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
for UTF-8. Ideally, this would be obtained by a target hook if it were
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
either to warn_format_trunc for bounded functions or to
warn_format_overflow otherwise. */
static int warn_level;
+struct call_info;
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
of output either a formatting function or an individual directive
can result in. */
static bool
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
string or a conversion specification that starts with '%'. */
/* Format conversion function that given a directive and an argument
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. */
bool get_flag (char chr) const
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.
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. */
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.
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
/* Description of a call to a formatted function. */
-struct sprintf_dom_walker::call_info
+struct call_info
{
/* Function call statement. */
gimple *callstmt;
/* Return the result of formatting a no-op directive (such as '%n'). */
static fmtresult
-format_none (const directive &, tree, vr_values *)
+format_none (const directive &, tree, const vr_values *)
{
fmtresult res (0);
return res;
/* Return the result of formatting the '%%' directive. */
static fmtresult
-format_percent (const directive &, tree, vr_values *)
+format_percent (const directive &, tree, const vr_values *)
{
fmtresult res (1);
return res;
static bool
get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax,
bool absolute, HOST_WIDE_INT negbound,
- class vr_values *vr_values)
+ const class vr_values *vr_values)
{
/* The type of the result. */
const_tree type = integer_type_node;
&& TYPE_PRECISION (argtype) <= TYPE_PRECISION (type))
{
/* 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))
{
HOST_WIDE_INT type_min
used when the directive argument or its value isn't known. */
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 uintmax_type_node;
{
/* Try to determine the range of values of the integer argument
(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))
{
argmin = vr->min ();
ARG. */
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] };
tree type = (dir.modifier == FMT_LEN_L || dir.modifier == FMT_LEN_ll
Used by the format_string function below. */
static fmtresult
-get_string_length (tree str, unsigned eltsize)
+get_string_length (tree str, unsigned eltsize, const vr_values *vr)
{
if (!str)
return fmtresult ();
- /* 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. */
+ /* Try to determine the dynamic string length first. */
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. */
- if (integer_all_onesp (lendata.maxbound)
+ /* Return the default result when nothing is known about the string. */
+ if (lendata.maxbound
+ && integer_all_onesp (lendata.maxbound)
&& integer_all_onesp (lendata.maxlen))
return fmtresult ();
: 0);
HOST_WIDE_INT max
- = (tree_fits_uhwi_p (lendata.maxbound)
+ = (lendata.maxbound && tree_fits_uhwi_p (lendata.maxbound)
? tree_to_uhwi (lendata.maxbound)
: HOST_WIDE_INT_M1U);
else
{
/* 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
- the shortest string and reset the lower bound to zero. */
+ set the likely length to the greater of 1. If MAXBOUND is
+ 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.min = 0;
+ if (lendata.maxbound)
+ res.range.min = 0;
}
res.range.unlikely = unbounded ? HOST_WIDE_INT_MAX : res.range.max;
vsprinf). */
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;
vsprinf). */
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;
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
&& slen.range.min < HOST_WIDE_INT_MAX)
{
/* Format plain string (part of the format string itself). */
static fmtresult
-format_plain (const directive &dir, tree, vr_values *)
+format_plain (const directive &dir, tree, const vr_values *)
{
fmtresult res (dir.len);
return res;
should be diagnosed given the AVAILable space in the destination. */
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)
{
if (result.max <= avail.min)
static bool
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 directive &dir)
{
in *RES. Return true if the directive has been handled. */
static bool
-format_directive (const sprintf_dom_walker::call_info &info,
+format_directive (const call_info &info,
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
of the format string. */
the directive. */
static size_t
-parse_directive (sprintf_dom_walker::call_info &info,
+parse_directive (call_info &info,
directive &dir, format_result *res,
const char *str, unsigned *argno,
- vr_values *vr_values)
+ const vr_values *vr_values)
{
const char *pcnt = strchr (str, target_percent);
dir.beg = str;
on, false otherwise (e.g., when a unknown or unhandled directive was seen
that caused the processing to be terminated early). */
-bool
-sprintf_dom_walker::compute_format_length (call_info &info,
- format_result *res)
+static bool
+compute_format_length (call_info &info, format_result *res, const vr_values *vr)
{
if (dump_file)
{
directive dir = directive ();
dir.dirno = dirno;
- size_t n = parse_directive (info, dir, res, pf, &argno,
- evrp_range_analyzer.get_vr_values ());
+ size_t n = parse_directive (info, dir, res, pf, &argno, vr);
/* Return failure if the format function fails. */
- if (!format_directive (info, res, dir,
- evrp_range_analyzer.get_vr_values ()))
+ if (!format_directive (info, res, dir, vr))
return false;
/* Return success the directive is zero bytes long and it's
of its return values. */
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,
unsigned HOST_WIDE_INT retval[2])
{
static bool
try_substitute_return_value (gimple_stmt_iterator *gsi,
- const sprintf_dom_walker::call_info &info,
+ const call_info &info,
const format_result &res)
{
tree lhs = gimple_get_lhs (info.callstmt);
static bool
try_simplify_call (gimple_stmt_iterator *gsi,
- const sprintf_dom_walker::call_info &info,
+ const call_info &info,
const format_result &res)
{
unsigned HOST_WIDE_INT dummy[2];
return tree_to_uhwi (fmtarg) - 1;
}
-/* Determine if a GIMPLE CALL 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. */
+} /* Unnamed namespace. */
+
+/* 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
-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 ();
info.callstmt = gsi_stmt (*gsi);
/* Try to determine the range of values of the argument
and use the greater of the two at level 1 and the smaller
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))
{
unsigned HOST_WIDE_INT minsize = TREE_INT_CST_LOW (vr->min ());
never set to true again). */
res.posunder4k = posunder4k && dstptr;
- bool success = compute_format_length (info, &res);
+ bool success = compute_format_length (info, &res, vr_values);
if (res.warned)
gimple_set_no_warning (info.callstmt, true);
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);
-}
NEXT_PASS (pass_build_cfg);
NEXT_PASS (pass_warn_function_return);
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_build_cgraph_edges);
TERMINATE_PASS_LIST (all_lowering_passes)
NEXT_PASS (pass_lower_vector_ssa);
NEXT_PASS (pass_lower_switch);
NEXT_PASS (pass_cse_reciprocals);
- NEXT_PASS (pass_sprintf_length, true);
NEXT_PASS (pass_reassoc, false /* insert_powi_p */);
NEXT_PASS (pass_strength_reduction);
NEXT_PASS (pass_split_paths);
NEXT_PASS (pass_object_sizes);
/* Fold remaining 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
to forward object-size and builtin folding results properly. */
NEXT_PASS (pass_copy_prop);
gcc_assert (strlen (print_rtx_head) < sizeof (indented_print_rtx_head) - 4);
snprintf (indented_print_rtx_head,
sizeof (indented_print_rtx_head),
- "%s ", print_rtx_head);
+ "%s ", print_rtx_head);
print_rtx_head = indented_print_rtx_head;
for (int i = 0; i < seq->len (); i++)
print_insn_with_notes (pp, seq->insn (i));
* gcc.target/mips/get-fcsr-3.c: New test.
+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
{
char des[3];
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 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 6 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 6 "strlen1" } } */
return __builtin_strlen (d);
}
-/* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 8 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 8 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strcmp" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strcmp" 0 "strlen1" } } */
return result;
}
-/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 1 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
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
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 \\(" 7 "strlen" { target { avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "\\*q_\[0-9\]* = 32;" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(\[^\n\r\]*, 1\\)" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 8 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" { target { avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "\\*q_\[0-9\]* = 32;" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(\[^\n\r\]*, 1\\)" 1 "strlen1" } } */
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
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 \\(" 6 "strlen" { target { avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" { target { avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
/* Where the memcpy is expanded, the assignemts to elements of l are
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.6. = " 1 "strlen" { 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\]*;" 3 "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 "strlen1" { 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 "strlen1" { target { avr-*-* } } } } */
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
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 \\(" 6 "strlen" { target { avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" { target { avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
/* Where the memcpy is expanded, the assignemts to elements of l are
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.1. = " 1 "strlen" { 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.6. = " 1 "strlen" { 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\]*;\[\n\r\]* l.0. = " 1 "strlen1" { 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 "strlen1" { 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 "strlen1" { target { avr-*-* } } } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
/* Compared to strlenopt-14gf.c, strcpy_chk with string literal as
second argument isn't being optimized by builtins.c into
memcpy. */
-/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__mempcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__mempcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
#define FORTIFY_SOURCE 2
#include "strlenopt-1.c"
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 5 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 5 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
#define USE_GNU
#include "strlenopt-22.c"
-/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
return len - len2;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
return fn1 (p, q);
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
#define FORTIFY_SOURCE 2
#include "strlenopt-2.c"
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 5 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 5 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { 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 4" 1 "optimized" } } */
/* { dg-final { scan-tree-dump-times "return 3" 1 "optimized" } } */
return (t1 == s);
}
-/* { dg-final { scan-tree-dump-times "__builtin_strncmp" 5 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_strncmp" 5 "strlen1" } } */
#define USE_GNU
#include "strlenopt-31.c"
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-not "strlen \\(" "strlen" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-not "strlen \\(" "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 3 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 3 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 3 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 3 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
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" } } */
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 "len = 18;" 1 "gimple" } }
{ dg-final { scan-tree-dump-times "lenx = 8;" 1 "gimple" } }
#define USE_GNU
#include "strlenopt-4.c"
-/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen1" } } */
#define FORTIFY_SOURCE 2
#include "strlenopt-4.c"
-/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen1" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
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" } } */
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" } } */
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" } } */
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" } } */
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 "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 "strcmp" 0 "gimple" } }
{ dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "optimized" } } */
}
-/* { 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 "abort" 0 "optimized" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
}
/* { dg-final { scan-tree-dump-not "failure" "optimized" } }
- { dg-final { scan-tree-dump-not "strlen" "gimple" } } */
+ { dg-final { scan-tree-dump-not "strlen1" "gimple" } } */
--- /dev/null
+/* 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);
+}
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "\\*r_\[0-9\]* = 0;" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
+/* { 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 0;" 2 "optimized" } } */
return 0;
}
-/* { dg-final { scan-tree-dump-times "strlen \\(" 5 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 5 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
/* { 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
rather than whatever the OS has in its headers. */
char *stpcpy (char *__restrict, const char *__restrict);
#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__
# define bos(ptr) __builtin_object_size (ptr, FORTIFY_SOURCE > 0)
# define bos0(ptr) __builtin_object_size (ptr, 0)
--- /dev/null
+/* 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" } } */
--- /dev/null
+/* 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" } } */
--- /dev/null
+/* 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" } } */
--- /dev/null
+/* 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" } } */
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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" } */
+}
--- /dev/null
+/* 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" } } */
__builtin_abort ();
}
-/* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen1" } } */
f (__builtin_strlen (s));
}
-/* { dg-final { scan-tree-dump-times "strlen" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen" 0 "strlen1" } } */
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_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_fold_builtins (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_post_ipa_warn (gcc::context *ctxt);
#include "intl.h"
#include "attribs.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
is an index into strinfo vector, negative value stands for
/* Number of currently active string indexes plus one. */
static int max_stridx;
+/* Set to true to optimize, false when just checking. */
+static bool strlen_optimize;
+
/* String information record. */
struct strinfo
{
/* Hash table for mapping decls to a chained list of offset -> idx
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
smaller than bound) calls to stridx instances describing
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
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)
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)
{
gimple *stmt = si->stmt, *lenstmt;
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
might change due to stores in stmt. */
&& 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
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
true. */
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);
if (is_gimple_call (stmt))
{
- tree callee = gimple_call_fndecl (stmt);
- if (valid_builtin_call (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:
- break;
- }
+ if (!strlen_check_and_optimize_call (gsi, rvals))
+ return false;
}
+ else if (!flag_optimize_strlen || !strlen_optimize)
+ return true;
else if (is_gimple_assign (stmt) && !gimple_clobber_p (stmt))
{
+ /* Handle non-clobbering assignment. */
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)
|| (gimple_assign_cast_p (stmt)
else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
handle_pointer_plus (gsi);
}
- else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (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 (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))
+ else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (lhs_type))
+ /* Handle assignment to a character. */
+ handle_integral_assign (gsi, cleanup_eh);
+ else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
{
tree type = TREE_TYPE (lhs);
if (TREE_CODE (type) == ARRAY_TYPE)
{
public:
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 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
execute function. */
bool m_cleanup_cfg;
edge
strlen_dom_walker::before_dom_children (basic_block bb)
{
+ evrp.enter (bb);
+
basic_block dombb = get_immediate_dominator (CDI_DOMINATORS, bb);
if (dombb == NULL)
/* Attempt to optimize individual statements. */
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))
m_cleanup_cfg = true;
void
strlen_dom_walker::after_dom_children (basic_block bb)
{
+ evrp.leave (bb);
+
if (bb->aux)
{
stridx_to_strinfo = ((vec<strinfo *, va_heap, vl_embed> *) bb->aux);
}
}
-/* Main entry point. */
-
namespace {
-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
+static unsigned int
+printf_strlen_execute (function *fun, bool warn_only)
{
-public:
- pass_strlen (gcc::context *ctxt)
- : gimple_opt_pass (pass_data_strlen, ctxt)
- {}
+ strlen_optimize = !warn_only;
- /* 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);
if (warn_stringop_overflow || warn_stringop_truncation)
strlen_to_stridx = new hash_map<tree, stridx_strlenloc> ();
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
tree and a forward walk of statements within each block. */
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 ();
strinfo_pool.release ();
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;
}
+/* 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
+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)
{
extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, 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
tree
value_range_base::type () const
{
- gcc_assert (m_min || undefined_p ());
+ gcc_assert (m_min);
return TREE_TYPE (min ());
}
called, if we are anyway, keep it VARYING. */
if (old_vr->varying_p ())
{
- new_vr->set_varying (new_vr->type ());
+ new_vr->set_varying (TREE_TYPE (var));
is_new = false;
}
else if (new_vr->undefined_p ())