This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] integrate sprintf pass into strlen (PR 83431)


...
So if I'm reading things correctly, it appears gimple-ssa-sprintf.c is
no longer a distinct pass.  Instead it co-exists with the strlen pass.
Right?

Yes.  strlen just calls into sprintf to handle the calls.

diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index a0934bcaf87..b05e4050f1d 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -683,7 +618,7 @@ fmtresult::type_max_digits (tree type, int base)
  static bool
  get_int_range (tree, HOST_WIDE_INT *, HOST_WIDE_INT *, bool, HOST_WIDE_INT,
-           class vr_values *vr_values);
+           const class vr_values *vr_values);
FWIW, I think this is something *I* could do a lot better at.
Specifically I think we're not supposed to be writing the "class" here
and I'm not as good as I should be with marking things const.  Thanks
for cleaning up my lack of consts.

I think you did the best you could given the APIs you had to work
with There's still plenty of room to improve const-correctness but
it involves changing other APIs outsid strlen/sprintf.

diff --git a/gcc/passes.def b/gcc/passes.def
index 9a5b0cd554a..637e228f988 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -42,7 +42,7 @@ along with GCC; see the file COPYING3.  If not see
    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_strlen, false);
So this is something we discussed a bit on the phone.  This is very
early in the pipeline -- before we've gone into SSA form.

I'm a bit concerned that we're running strlen that early without some
kind of auditing of whether or not the strlen pass can safely run that
early.  Similarly have we done any review for issues that might arise
from running strlen more than once?  I guess in some small way
encapsulating the state into a class like my unsubmitted patch does
would help there.

The strlen optimization machinery only runs once.  The code avoids
running it when the pass is invoked early and only calls into sprintf
to do format checking.


More generally I think we concluded that the placement of sprintf this
early was driven by the early placement of walloca.  I don't want to
open a huge can of worms here, but do we really need to run this early
in the pipeline?

We decided to run it early when optimization is disabled because
there's a good amount of code that can be checked even without
ranges and string lengths (especially at the conservative level
2 setting when we consider the largest integers and array sizes
instead of values or string lengths).

For example, this is diagnosed for the potential buffer overflow
at -Wformat-overflow=2 even without optimization:

   char a[8], s[4];

   void f (int i)
   {
     __builtin_sprintf (a, "%s = %i", s, i);
   }

  warning: ‘%i’ directive writing between 1 and 11 bytes into a region of size between 2 and 5 [-Wformat-overflow=]


diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 74cd6c44874..89932713476 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -55,6 +55,12 @@ along with GCC; see the file COPYING3.  If not see
  #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"
Nit: Drop the extra newline.

@@ -604,14 +614,19 @@ set_endptr_and_length (location_t loc, strinfo *si, tree endptr)
    si->full_string_p = true;
  }
-/* Return string length, or NULL if it can't be computed.  */
+/* Return the constant string length, or NULL if it can't be computed.  */>
  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;
IIUC get_string_length could return a non-constant expression that gives
the runtime length of a string.   So I think the comment change is a bit
misleading.

Yes, good catch, thanks!

Nit: Use NULL rather than null.  I think this happens in more than one
place in your patch.  Similarly I think we generally use NUL rather than
nul when referring to a single character.
The term is a "null pointer."  NULL is a C macro that has in C++ 11
been superseded by nullptr.  I don't mind using NUL character but
I also don't think it matters.  No one will be confused about what
either means.

+/* 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.  */
+
+static bool
+get_range_strlen_dynamic (tree src, c_strlen_data *pdata, bitmap *visited,
+              const vr_values *rvals)
[ ... ]
We've already touched on the need to limit the backwards walk out of
this code.  Limiting based on the product of # phi args * number of phis
visited, recursion depth, or number of SSA_NAME_DEF_STMTs visited all
seem reasonable to me.

I do think Richi's suggestion for figuring out a suitable inflection
point is reasonable.

It's easy enough to add here.  But I know I've introduced other
algorithms that recurse on SSA_NAME_DEF_STMT, and I'm quite sure
others predate those.  To make a difference I think we need to
review at least the one most likely to be exposed to this problem
and introduce the same limit there.  I could probably fix the ones
I wrote reasonably quickly, but making the change to the others
would be much more of a project.  I looked to see how pervasive
this might be and here is just a small sampling of things that
jumped out at me in a quick grep search:

  *  compute_builtin_object_size (for _FORTIFY_SOURCE)
  *  compute_objsize (for -Wstringop-overflow)
  *  get_range_strlen
  *  maybe_fold_{and,or}_comparisons in gimple-fold.c
  *  -Warray-bounds (computing an offset into an object)
  *  -Wrestrict (computing an offset into an object)
  *  -Wreturn-local-addr (is_addr_local)
  *  -Wuninitialized (collect_phi_def_edges)

Given how wide-spread this technique seems to be, if the recursion
is in fact a problem it's just as likely (if not more) to come up
in the folder or in BOS or some other place as it is here.  So if
it needs fixing it seems it should be done as its own project and
everywhere (or as close as we can get), and not as part of this
integration.

  +  if (strinfo *si = get_strinfo (idx))
+    {
+      pdata->minlen = get_string_length (si);
+      if (!pdata->minlen
+      && si->nonzero_chars)
Nit: No need for a line break in that conditional.



+
+/* 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 ARG, or the size of the largest array ARG 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)
I think you need to s/ARG/SRC/ in the function comment since SRC is the
name of the parameter.

Yes, thanks.


@@ -3703,84 +4031,231 @@ fold_strstr_to_strncmp (tree rhs1, tree rhs2, gimple *stmt)
      }
  }
+/* 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
+check_and_optimize_call (gimple_stmt_iterator *gsi, const vr_values *rvals)
It was suggested that perhaps we should prefix this call name, but I
think the better solution might be to classify the pass and make this a
member function.  That would seem to naturally fall to me since I've got
a classification patch for this code from last year that I could easily
update after your patch.

Well, sure.  The whole pass can be a class (or a set of classes).
It began as C code and then C++ started to slowly and organically
creep in.  There are many other nice improvements we could make
by putting C++ to better use.  One very simple one I'd like is
moving local variable declarations to the point of their
initialization.  Making the APIs const-correct would also improve
readability.  But I've resisted making these changes because I
know people are sensitive to too much churn.  If you think it's
a good idea for me to make these changes let me know.  I'd be
happy to do it, just separately from this integration.

+{
+  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;
+    }
Shouldn't the guard here be similar, if not the same as the gate for the
old sprintf pass?  Which was:

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);
-}

This test is now integrated into pass_strlen::gate so the guard
above is only entered when the pass is running early (i.e., at
-O0) or when the strlen optimization is disabled.  Otherwise
there's another call to handle_printf_call in the big switch
statement in check_and_optimize_call.

@@ -4119,7 +4504,10 @@ const pass_data pass_data_strlen =
    "strlen", /* name */
    OPTGROUP_NONE, /* optinfo_flags */
    TV_TREE_STRLEN, /* tv_id */
-  ( PROP_cfg | PROP_ssa ), /* properties_required */
+  /* Normally the pass would require PROP_ssa but because it also
+     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 */
@@ -4128,20 +4516,50 @@ const pass_data pass_data_strlen =
So the question I'd come back to is what are we capturing with the
instance that runs before we're in SSA form and can we reasonably catch
that stuff after going into SSA form?

It may be that we went through this at the initial submission of the
sprintf patches.  I simply can't remember.

Please see my answer + example above.

    /* opt_pass methods: */
-  virtual bool gate (function *) { return flag_optimize_strlen != 0; }
+
+  opt_pass * clone () {
+    return new pass_strlen (m_ctxt);
+  }
Nit.  I think this is trivial enough to just have on a single line and
is generally consistent with other passes.


+
+  virtual bool gate (function *);
    virtual unsigned int execute (function *);
+  void set_pass_param (unsigned int n, bool param)
+    {
+      gcc_assert (n == 0);
+      do_optimize = param;
+    }
  }; // class pass_strlen
+
+bool
+pass_strlen::gate (function *)
+{
+  /* Run the pass iff -Warn-format-overflow or -Warn-format-truncation
Aren't these Wformat-overflow and Wformat-trunction?

Yes, thanks.



+     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_optimize_strlen > 0
+       || flag_printf_return_value)
+      && (optimize > 0) == do_optimize);
+}
Ah, this is where the sprintf gateing clause went.



+
  unsigned int
  pass_strlen::execute (function *fun)
  {
+  strlen_optimize = do_optimize;
+
    gcc_assert (!strlen_to_stridx);
    if (warn_stringop_overflow || warn_stringop_truncation)
      strlen_to_stridx = new hash_map<tree, stridx_strlenloc> ();
@@ -4151,10 +4569,17 @@ pass_strlen::execute (function *fun)
    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 ();
@@ -4175,6 +4600,15 @@ pass_strlen::execute (function *fun)
        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;
  }
Is scev really that useful here?  If it is, fine, if not I'd rather not
pay the price to set it up.

It was introduced by Aldy to fix PR 85598.


My brain is turning to mush, so I think I'm going to need to do another
iteration over this patch.

I want to respond because it's been a while but I'll post an updated
patch later this week.  In the meantime, if you have more comments
on the rest of it please send them my way.

Attached is the updated patch.  I added the recursion limit to
get_range_strlen_dynamic (and tests for it) but not to
get_range_strlen in gimple-fold.c or any of the other functions
outside tree-ssa-strlen.c.  I will do that separately, once this
work has been committed.

Rested on x86_64-linux.

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

gcc/ChangeLog:

	PR c++/83431
	* gimple-ssa-sprintf.c (pass_data_sprintf_length): Remove object.
	(sprintf_dom_walker): Remove class.
	(get_int_range): Make argument const.
	(directive::fmtfunc, directive::set_precision): Same.
	(format_none): Same.
	(build_intmax_type_nodes): Same.
	(adjust_range_for_overflow): Same.
	(format_floating): Same.
	(format_character): Same.
	(format_string): Same.
	(format_plain): Same.
	(get_int_range): Cast away constness.
	(format_integer): Same.
	(get_string_length): Call get_range_strlen_dynamic.  Handle
	null lendata.maxbound.
	(should_warn_p): Adjust argument scope qualifier.
	(maybe_warn): Same.
	(format_directive): Same.
	(parse_directive): Same.
	(is_call_safe): Same.
	(try_substitute_return_value): Same.
	(sprintf_dom_walker::handle_printf_call): Rename...
	(handle_printf_call): ...to this.  Initialize target to host charmap
	here instead of in pass_sprintf_length::execute.
	(struct call_info): Make global.
	(sprintf_dom_walker::compute_format_length): Make global.
	(sprintf_dom_walker::handle_gimple_call): Same.
	* passes.def (pass_sprintf_length): Replace with pass_strlen.
	* print-rtl.c (print_pattern): Reduce the number of spaces to
	avoid -Wformat-truncation.
	* tree-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): Rename...
	(check_and_optimize_stmt): ...to this.  Factor code out into
	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.
	(pass_data_strlen): Remove property not satisfied during an early run.
	(pass_strlen::do_optimize): New data member.
	(pass_strlen::set_pass_param): New member function.
	(pass_strlen::gate): Update to handle printf calls.
	(pass_strlen::execute): Initialize loop and scev optimizers.
	(dump_strlen_info): New function.
	* tree-ssa-strlen.h (get_range_strlen_dynamic): Declare.
	(handle_printf_call): Same.

gcc/testsuite/ChangeLog:

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


diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index 6ba3d86f211..f08e586c0ef 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -85,7 +85,7 @@ along with GCC; see the file COPYING3.  If not see
 #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
@@ -100,80 +100,15 @@ along with GCC; see the file COPYING3.  If not see
 
 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.  */
@@ -684,7 +619,7 @@ fmtresult::type_max_digits (tree type, int base)
 
 static bool
 get_int_range (tree, HOST_WIDE_INT *, HOST_WIDE_INT *, bool, HOST_WIDE_INT,
-	       class vr_values *vr_values);
+	       const class vr_values *vr_values);
 
 /* Description of a format directive.  A directive is either a plain
    string or a conversion specification that starts with '%'.  */
@@ -719,7 +654,7 @@ struct directive
 
   /* 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
@@ -756,7 +691,7 @@ struct directive
      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_values)
   {
     get_int_range (arg, width, width + 1, true, 0, vr_values);
   }
@@ -772,7 +707,7 @@ struct directive
      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_values)
   {
     get_int_range (arg, prec, prec + 1, false, -1, vr_values);
   }
@@ -904,7 +839,7 @@ bytes_remaining (unsigned HOST_WIDE_INT navail, const format_result &res)
 
 /* Description of a call to a formatted function.  */
 
-struct sprintf_dom_walker::call_info
+struct call_info
 {
   /* Function call statement.  */
   gimple *callstmt;
@@ -978,7 +913,7 @@ struct sprintf_dom_walker::call_info
 /* 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;
@@ -987,7 +922,7 @@ format_none (const directive &, tree, vr_values *)
 /* 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;
@@ -1047,7 +982,7 @@ build_intmax_type_nodes (tree *pintmax, tree *puintmax)
 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;
@@ -1086,7 +1021,8 @@ get_int_range (tree arg, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax,
 	  && TYPE_PRECISION (argtype) <= TYPE_PRECISION (type))
 	{
 	  /* Try to determine the range of values of the integer argument.  */
-	  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
@@ -1203,7 +1139,7 @@ adjust_range_for_overflow (tree dirtype, tree *argmin, tree *argmax)
    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;
@@ -1386,7 +1322,8 @@ format_integer (const directive &dir, tree arg, vr_values *vr_values)
     {
       /* Try to determine the range of values of the integer argument
 	 (range information is not available for pointers).  */
-      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 ();
@@ -1836,7 +1773,7 @@ format_floating (const directive &dir, const HOST_WIDE_INT prec[2])
    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
@@ -2030,21 +1967,33 @@ format_floating (const directive &dir, tree arg, vr_values *)
    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_values)
 {
   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_values);
+  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);
+    }
 
-  /* Return the default result when nothing is known about the string. */
-  if (integer_all_onesp (lendata.maxbound)
+  /* 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 (lendata.maxbound
+      && integer_all_onesp (lendata.maxbound)
       && integer_all_onesp (lendata.maxlen))
     return fmtresult ();
 
@@ -2054,7 +2003,7 @@ get_string_length (tree str, unsigned eltsize)
        : 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);
 
@@ -2093,10 +2042,11 @@ get_string_length (tree str, unsigned eltsize)
   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;
@@ -2110,7 +2060,7 @@ get_string_length (tree str, unsigned eltsize)
    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;
 
@@ -2186,7 +2136,7 @@ format_character (const directive &dir, tree arg, vr_values *vr_values)
    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;
 
@@ -2204,7 +2154,7 @@ format_string (const directive &dir, tree arg, vr_values *)
       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)
     {
@@ -2376,7 +2326,7 @@ format_string (const directive &dir, tree arg, vr_values *)
 /* 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;
@@ -2386,7 +2336,7 @@ format_plain (const directive &dir, tree, vr_values *)
    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)
@@ -2457,7 +2407,7 @@ should_warn_p (const sprintf_dom_walker::call_info &info,
 
 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)
 {
@@ -2737,9 +2687,9 @@ maybe_warn (substring_loc &dirloc, location_t argloc,
    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.  */
@@ -3086,10 +3036,10 @@ format_directive (const sprintf_dom_walker::call_info &info,
    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;
@@ -3526,9 +3476,8 @@ parse_directive (sprintf_dom_walker::call_info &info,
    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)
     {
@@ -3564,12 +3513,10 @@ sprintf_dom_walker::compute_format_length (call_info &info,
       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
@@ -3617,7 +3564,7 @@ get_destination_size (tree dest)
    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])
 {
@@ -3676,7 +3623,7 @@ is_call_safe (const sprintf_dom_walker::call_info &info,
 
 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);
@@ -3794,7 +3741,7 @@ try_substitute_return_value (gimple_stmt_iterator *gsi,
 
 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];
@@ -3847,13 +3794,17 @@ get_user_idx_format (tree fndecl, unsigned *idx_args)
   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);
@@ -4119,7 +4070,8 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *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.  */
-	  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 ());
@@ -4230,7 +4182,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
      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);
 
@@ -4256,71 +4208,3 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
 
   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);
-}
diff --git a/gcc/passes.def b/gcc/passes.def
index 1a7fd144f87..50e19e0cbb1 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -42,7 +42,7 @@ along with GCC; see the file COPYING3.  If not see
   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_strlen, /*do_optimize=*/false);
   NEXT_PASS (pass_walloca, /*strict_mode_p=*/true);
   NEXT_PASS (pass_build_cgraph_edges);
   TERMINATE_PASS_LIST (all_lowering_passes)
@@ -307,7 +307,6 @@ along with GCC; see the file COPYING3.  If not see
       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);
@@ -315,7 +314,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_fre, false /* may_iterate */);
       NEXT_PASS (pass_thread_jumps);
       NEXT_PASS (pass_dominator, false /* may_peel_loop_headers_p */);
-      NEXT_PASS (pass_strlen);
+      NEXT_PASS (pass_strlen, /*do_optimize=*/true);
       NEXT_PASS (pass_thread_jumps);
       NEXT_PASS (pass_vrp, false /* warn_array_bounds_p */);
       /* Threading can leave many const/copy propagations in the IL.
@@ -356,7 +355,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_object_sizes);
       /* Fold remaining builtins.  */
       NEXT_PASS (pass_fold_builtins);
-      NEXT_PASS (pass_sprintf_length, true);
+      NEXT_PASS (pass_strlen, /*do_optimize=*/true);
       /* Copy propagation also copy-propagates constants, this is necessary
          to forward object-size and builtin folding results properly.  */
       NEXT_PASS (pass_copy_prop);
diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c
index 10948efddd9..b96ab5e0544 100644
--- a/gcc/print-rtl.c
+++ b/gcc/print-rtl.c
@@ -1815,7 +1815,7 @@ print_pattern (pretty_printer *pp, const_rtx x, int verbose)
 	    gcc_assert (strlen (print_rtx_head) < sizeof (indented_print_rtx_head) - 4);
 	    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));
diff --git a/gcc/testsuite/gcc.dg/pr79538.c b/gcc/testsuite/gcc.dg/pr79538.c
index 6cdab45128a..4f10d97395e 100644
--- a/gcc/testsuite/gcc.dg/pr79538.c
+++ b/gcc/testsuite/gcc.dg/pr79538.c
@@ -17,6 +17,6 @@ void f ()
 {
   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;
 }
diff --git a/gcc/testsuite/gcc.dg/pr81292-1.c b/gcc/testsuite/gcc.dg/pr81292-1.c
index 931e4c37c17..9a19e6b001b 100644
--- a/gcc/testsuite/gcc.dg/pr81292-1.c
+++ b/gcc/testsuite/gcc.dg/pr81292-1.c
@@ -32,4 +32,4 @@ main (void)
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/pr81292-2.c b/gcc/testsuite/gcc.dg/pr81292-2.c
index c1c507f982e..4caec6228d4 100644
--- a/gcc/testsuite/gcc.dg/pr81292-2.c
+++ b/gcc/testsuite/gcc.dg/pr81292-2.c
@@ -32,4 +32,4 @@ main (void)
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 6 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 6 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/pr81703.c b/gcc/testsuite/gcc.dg/pr81703.c
index 190f4a833dd..fb4aab5ad60 100644
--- a/gcc/testsuite/gcc.dg/pr81703.c
+++ b/gcc/testsuite/gcc.dg/pr81703.c
@@ -9,4 +9,4 @@ unsigned g (void)
   return __builtin_strlen (d);
 }
 
-/* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strcmpopt_2.c b/gcc/testsuite/gcc.dg/strcmpopt_2.c
index 0131b8f7d69..7459842b91c 100644
--- a/gcc/testsuite/gcc.dg/strcmpopt_2.c
+++ b/gcc/testsuite/gcc.dg/strcmpopt_2.c
@@ -64,4 +64,4 @@ int main (void)
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 8 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 8 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strcmpopt_3.c b/gcc/testsuite/gcc.dg/strcmpopt_3.c
index 86a0d7a08b3..f8474d90eea 100644
--- a/gcc/testsuite/gcc.dg/strcmpopt_3.c
+++ b/gcc/testsuite/gcc.dg/strcmpopt_3.c
@@ -28,4 +28,4 @@ int main (void)
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strcmp" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strcmp" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strcmpopt_4.c b/gcc/testsuite/gcc.dg/strcmpopt_4.c
index d727bc363e5..64ad1d8eac0 100644
--- a/gcc/testsuite/gcc.dg/strcmpopt_4.c
+++ b/gcc/testsuite/gcc.dg/strcmpopt_4.c
@@ -13,4 +13,4 @@ f1 (S * s)
   return result;
 }
 
-/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 1 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-1.c b/gcc/testsuite/gcc.dg/strlenopt-1.c
index 910ec672e96..feec97c8821 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-1.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-1.c
@@ -36,9 +36,9 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-10.c b/gcc/testsuite/gcc.dg/strlenopt-10.c
index 97167df959f..5956f07274f 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-10.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-10.c
@@ -69,14 +69,14 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen2" } } */
 /* 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 "strlen2" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen2" { target { avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "\\*q_\[0-9\]* = 32;" 1 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(\[^\n\r\]*, 1\\)" 1 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-11.c b/gcc/testsuite/gcc.dg/strlenopt-11.c
index f7fa44bde35..c21241cf3c4 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-11.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-11.c
@@ -58,18 +58,18 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 3 "strlen2" } } */
 /* 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 "strlen2" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen2" { target { avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
 /* 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 "strlen2" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.6. = " 1 "strlen2" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.9. = " 1 "strlen2" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;" 3 "strlen2" { target { avr-*-* } } } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-13.c b/gcc/testsuite/gcc.dg/strlenopt-13.c
index 3502599b28c..1930fa8f403 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-13.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-13.c
@@ -55,19 +55,19 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 4 "strlen2" } } */
 /* 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 "strlen2" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen2" { target { avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
 /* 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 "strlen2" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.1. = " 1 "strlen2" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.5. = " 1 "strlen2" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.6. = " 1 "strlen2" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;" 4 "strlen2" { target { avr-*-* } } } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-14g.c b/gcc/testsuite/gcc.dg/strlenopt-14g.c
index 62a83bf8fd7..40b24a7f632 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-14g.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-14g.c
@@ -107,10 +107,10 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-14gf.c b/gcc/testsuite/gcc.dg/strlenopt-14gf.c
index 8b126fcb7ea..2d2ea045c0a 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-14gf.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-14gf.c
@@ -11,15 +11,15 @@
 /* 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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__mempcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-15.c b/gcc/testsuite/gcc.dg/strlenopt-15.c
index 827ea07b6ea..b712bdfad4c 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-15.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-15.c
@@ -51,9 +51,9 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-16g.c b/gcc/testsuite/gcc.dg/strlenopt-16g.c
index 0cf8410735a..3a7fd4be391 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-16g.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-16g.c
@@ -24,10 +24,10 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-17g.c b/gcc/testsuite/gcc.dg/strlenopt-17g.c
index 184e530788e..c7efd9052f5 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-17g.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-17g.c
@@ -47,10 +47,10 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-18g.c b/gcc/testsuite/gcc.dg/strlenopt-18g.c
index f734675ec40..578a1625caf 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-18g.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-18g.c
@@ -73,9 +73,9 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-19.c b/gcc/testsuite/gcc.dg/strlenopt-19.c
index 022ba8b4787..cdeaf7eed24 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-19.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-19.c
@@ -72,9 +72,9 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-1f.c b/gcc/testsuite/gcc.dg/strlenopt-1f.c
index 856774de7ad..02e69c24e5b 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-1f.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-1f.c
@@ -5,13 +5,13 @@
 #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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-2.c b/gcc/testsuite/gcc.dg/strlenopt-2.c
index fd59a3cd513..d42de623da1 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-2.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-2.c
@@ -40,9 +40,9 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 5 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-20.c b/gcc/testsuite/gcc.dg/strlenopt-20.c
index 7b483eaeac1..c0ecbbccf4a 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-20.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-20.c
@@ -86,9 +86,9 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-21.c b/gcc/testsuite/gcc.dg/strlenopt-21.c
index 05b85a49dde..3a57403e7ab 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-21.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-21.c
@@ -57,9 +57,9 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-22.c b/gcc/testsuite/gcc.dg/strlenopt-22.c
index b4ef772f0e5..e2933e158e7 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-22.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-22.c
@@ -31,9 +31,9 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-22g.c b/gcc/testsuite/gcc.dg/strlenopt-22g.c
index 9c5d020588f..67850fbe6c9 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-22g.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-22g.c
@@ -5,9 +5,9 @@
 #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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-24.c b/gcc/testsuite/gcc.dg/strlenopt-24.c
index 639501a53e0..d4a3b5a818d 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-24.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-24.c
@@ -13,4 +13,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-25.c b/gcc/testsuite/gcc.dg/strlenopt-25.c
index 89b60e3ebed..aab0026be05 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-25.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-25.c
@@ -14,4 +14,4 @@ main ()
   return len - len2;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-26.c b/gcc/testsuite/gcc.dg/strlenopt-26.c
index 6bb0263d315..8016e7dfb38 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-26.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-26.c
@@ -20,5 +20,5 @@ main (void)
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-27.c b/gcc/testsuite/gcc.dg/strlenopt-27.c
index c539edb821c..8c1762e3e6b 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-27.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-27.c
@@ -19,4 +19,4 @@ main (void)
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-28.c b/gcc/testsuite/gcc.dg/strlenopt-28.c
index 03fb01781bd..4cddc906a57 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-28.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-28.c
@@ -56,4 +56,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-29.c b/gcc/testsuite/gcc.dg/strlenopt-29.c
index fb4b4c9cc71..d7406e8e2f4 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-29.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-29.c
@@ -24,4 +24,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-2f.c b/gcc/testsuite/gcc.dg/strlenopt-2f.c
index 1e915dac928..addab117b4b 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-2f.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-2f.c
@@ -5,13 +5,13 @@
 #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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 5 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-3.c b/gcc/testsuite/gcc.dg/strlenopt-3.c
index f17779c0a66..9934df3cde1 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-3.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-3.c
@@ -53,12 +53,12 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
 /* { 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" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-30.c b/gcc/testsuite/gcc.dg/strlenopt-30.c
index a85df686ce2..75f28439f8a 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-30.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-30.c
@@ -60,4 +60,4 @@ _Bool f7(char *s)
   return (t1 == s);
 }
 
-/* { dg-final { scan-tree-dump-times "__builtin_strncmp" 5 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_strncmp" 5 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-31g.c b/gcc/testsuite/gcc.dg/strlenopt-31g.c
index 45cc29c1024..957f028d088 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-31g.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-31g.c
@@ -4,6 +4,6 @@
 #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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen2" } } */
+/* { dg-final { scan-tree-dump-not "strlen \\(" "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-32.c b/gcc/testsuite/gcc.dg/strlenopt-32.c
index 08eb6bc2b08..c021aecd0a6 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-32.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-32.c
@@ -190,4 +190,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-33.c b/gcc/testsuite/gcc.dg/strlenopt-33.c
index 1e1c4dee1f8..1d75d30c36d 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-33.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-33.c
@@ -39,4 +39,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-33g.c b/gcc/testsuite/gcc.dg/strlenopt-33g.c
index 7d24d2bfc32..8bc027aefb6 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-33g.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-33g.c
@@ -40,5 +40,5 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-34.c b/gcc/testsuite/gcc.dg/strlenopt-34.c
index c9433c0399e..84116293382 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-34.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-34.c
@@ -35,4 +35,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-35.c b/gcc/testsuite/gcc.dg/strlenopt-35.c
index 03b3e13a8e2..6c17f9c1175 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-35.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-35.c
@@ -28,4 +28,4 @@ main ()
   return 0;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-4.c b/gcc/testsuite/gcc.dg/strlenopt-4.c
index 802e4ca7ce4..9f48646bef4 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-4.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-4.c
@@ -66,9 +66,9 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 3 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 3 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-48.c b/gcc/testsuite/gcc.dg/strlenopt-48.c
index 179edd82a43..dd556c817ac 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-48.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-48.c
@@ -31,5 +31,5 @@ void h (void)
     abort();
 }
 
-/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } }
+/* { dg-final { scan-tree-dump-times "strlen2" 0 "optimized" } }
    { dg-final { scan-tree-dump-times "abort" 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-49.c b/gcc/testsuite/gcc.dg/strlenopt-49.c
index f901fd14b54..7da553159c5 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-49.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-49.c
@@ -45,7 +45,7 @@ int cmp88 (void)
   return cmp88;
 }
 
-/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
+/* { dg-final { scan-tree-dump-times "strlen2" 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" } }
diff --git a/gcc/testsuite/gcc.dg/strlenopt-4g.c b/gcc/testsuite/gcc.dg/strlenopt-4g.c
index 879d5666c90..f53964677e0 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-4g.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-4g.c
@@ -5,9 +5,9 @@
 #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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-4gf.c b/gcc/testsuite/gcc.dg/strlenopt-4gf.c
index 7f261b7d34d..66afc11bf5a 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-4gf.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-4gf.c
@@ -6,13 +6,13 @@
 #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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-5.c b/gcc/testsuite/gcc.dg/strlenopt-5.c
index a24aea44e8b..37fe4e221da 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-5.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-5.c
@@ -48,9 +48,9 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-50.c b/gcc/testsuite/gcc.dg/strlenopt-50.c
index 1d1d36808a9..59b990c9755 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-50.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-50.c
@@ -112,5 +112,5 @@ void test_array_ref (void)
   T (&b[16], 0);  T (&b[17], 0);  T (&b[18], 0);  T (&b[19], 0);
 }
 
-/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
+/* { dg-final { scan-tree-dump-times "strlen2" 0 "gimple" } }
    { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-51.c b/gcc/testsuite/gcc.dg/strlenopt-51.c
index 3d879f1329a..9c0b15801b0 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-51.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-51.c
@@ -84,4 +84,4 @@ void test_elim_a9_9 (unsigned i)
   T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8);
 }
 
-/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "strlen2" 0 "gimple" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-52.c b/gcc/testsuite/gcc.dg/strlenopt-52.c
index 03e063b435e..775f33dddee 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-52.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-52.c
@@ -284,5 +284,5 @@ void test_global_struct_struct_array (void)
   T (ssa[5].sa9[3].a6, 3);
 }
 
-/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
+/* { dg-final { scan-tree-dump-times "strlen2" 0 "gimple" } }
    { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-53.c b/gcc/testsuite/gcc.dg/strlenopt-53.c
index baa680d125b..8f514cadfac 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-53.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-53.c
@@ -112,5 +112,5 @@ void test_array_ref (void)
   T (&b[16], 0);  T (&b[17], 0);  T (&b[18], 0);  T (&b[19], 0);
 }
 
-/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
+/* { dg-final { scan-tree-dump-times "strlen2" 0 "gimple" } }
    { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-54.c b/gcc/testsuite/gcc.dg/strlenopt-54.c
index c38e7918f8b..ccf6e349046 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-54.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-54.c
@@ -105,5 +105,5 @@ void elim_after_init_memcpy (void)
   T ("AB\000CD", 0, "ab\000c", 4, 2);
 }
 
-/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } }
+/* { dg-final { scan-tree-dump-times "strlen2" 0 "optimized" } }
    { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-55.c b/gcc/testsuite/gcc.dg/strlenopt-55.c
index d5a02953d36..9696a4b87a2 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-55.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-55.c
@@ -224,7 +224,7 @@ const void test_large_string_size (void)
 }
 
 
-/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
+/* { dg-final { scan-tree-dump-times "strlen2" 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" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-56.c b/gcc/testsuite/gcc.dg/strlenopt-56.c
index 39a532bf8d4..2bf99bde543 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-56.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-56.c
@@ -45,6 +45,6 @@ void test_contents (void)
 }
 
 
-/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
+/* { dg-final { scan-tree-dump-times "strlen2" 0 "gimple" } }
    { dg-final { scan-tree-dump-times "strcmp" 0 "gimple" } }
    { dg-final { scan-tree-dump-times "abort" 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-6.c b/gcc/testsuite/gcc.dg/strlenopt-6.c
index fbff14c4fdb..a6eab9c08dc 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-6.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-6.c
@@ -77,9 +77,9 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-61.c b/gcc/testsuite/gcc.dg/strlenopt-61.c
index 4f8e9c053e4..97790ac1fae 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-61.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-61.c
@@ -215,4 +215,4 @@ void test_ta2 (void)
 }
 
 /* { dg-final { scan-tree-dump-not "failure" "optimized" } }
-   { dg-final { scan-tree-dump-not "strlen" "gimple" } } */
+   { dg-final { scan-tree-dump-not "strlen2" "gimple" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-68.c b/gcc/testsuite/gcc.dg/strlenopt-68.c
new file mode 100644
index 00000000000..56d314e5d45
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-68.c
@@ -0,0 +1,382 @@
+/* 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);
+}
diff --git a/gcc/testsuite/gcc.dg/strlenopt-7.c b/gcc/testsuite/gcc.dg/strlenopt-7.c
index aa53d7e7525..7e27952dec6 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-7.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-7.c
@@ -40,12 +40,12 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "\\*r_\[0-9\]* = 0;" 1 "strlen2" } } */
 /* { dg-final { scan-tree-dump-times "return 3;" 1 "optimized" } } */
 /* { dg-final { scan-tree-dump-times "return 0;" 2 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-8.c b/gcc/testsuite/gcc.dg/strlenopt-8.c
index 85c6d38a965..ed2215d73ba 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-8.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-8.c
@@ -47,10 +47,10 @@ main ()
    into and end up with a short typed load / store which strlenopt is not
    able to analyze.  */
 
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" { xfail non_strict_align } } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" { target { non_strict_align } } } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" { target { ! non_strict_align } } } }  */
-/* { 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 "strlen2" { xfail non_strict_align } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen2" { target { non_strict_align } } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen2" { target { ! non_strict_align } } } }  */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-9.c b/gcc/testsuite/gcc.dg/strlenopt-9.c
index e8ff1023d71..613f45b2cc9 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-9.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-9.c
@@ -98,10 +98,10 @@ main ()
   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 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen2" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen2" } } */
 /* { dg-final { scan-tree-dump-times "return 4;" 1 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt.h b/gcc/testsuite/gcc.dg/strlenopt.h
index d25e08a8a42..518d0cf08b2 100644
--- a/gcc/testsuite/gcc.dg/strlenopt.h
+++ b/gcc/testsuite/gcc.dg/strlenopt.h
@@ -1,4 +1,4 @@
-/* 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.  */
 
@@ -25,6 +25,9 @@ void *mempcpy (void *__restrict, const void *__restrict, size_t);
 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)
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-6.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-6.c
new file mode 100644
index 00000000000..0d9b27505ec
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-6.c
@@ -0,0 +1,139 @@
+/* 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" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-7.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-7.c
new file mode 100644
index 00000000000..bf5072e955c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-7.c
@@ -0,0 +1,152 @@
+/* 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" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-8.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-8.c
new file mode 100644
index 00000000000..95b0b44bcf0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-8.c
@@ -0,0 +1,41 @@
+/* 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" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-9.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-9.c
new file mode 100644
index 00000000000..9c238ce8bcc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-9.c
@@ -0,0 +1,163 @@
+/* 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" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-5.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-5.c
new file mode 100644
index 00000000000..becba05277b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-5.c
@@ -0,0 +1,140 @@
+/* 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);
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-21.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-21.c
new file mode 100644
index 00000000000..41f932a41a8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-21.c
@@ -0,0 +1,94 @@
+/* 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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/dump-4.c b/gcc/testsuite/gcc.dg/tree-ssa/dump-4.c
new file mode 100644
index 00000000000..9377ed4f420
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/dump-4.c
@@ -0,0 +1,11 @@
+/* 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" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr83501.c b/gcc/testsuite/gcc.dg/tree-ssa/pr83501.c
index d8d3bf6039a..36a14205f78 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr83501.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr83501.c
@@ -11,4 +11,4 @@ void f (void)
     __builtin_abort ();
 }
 
-/* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_strlen" "strlen2" } } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 88b6bd7869e..fbaae289bd7 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -55,6 +55,12 @@ along with GCC; see the file COPYING3.  If not see
 #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
@@ -64,6 +70,9 @@ static vec<int> ssa_ver_to_stridx;
 /* 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
 {
@@ -154,7 +163,8 @@ struct decl_stridxlist_map
 
 /* 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
@@ -604,14 +614,21 @@ set_endptr_and_length (location_t loc, strinfo *si, tree endptr)
   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;
@@ -702,6 +719,336 @@ get_string_length (strinfo *si)
   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.  */
 
@@ -3707,84 +4054,231 @@ fold_strstr_to_strncmp (tree rhs1, tree rhs2, gimple *stmt)
     }
 }
 
+/* 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
+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 (!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)
@@ -3796,124 +4290,16 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh)
 	  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)
-	    type = TREE_TYPE (type);
-	  if (TREE_CODE (type) == INTEGER_TYPE
-	      && TYPE_MODE (type) == TYPE_MODE (char_type_node)
-	      && TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node))
+	  if (TREE_CODE (lhs_type) == ARRAY_TYPE)
+	    lhs_type = TREE_TYPE (lhs_type);
+	  if (TREE_CODE (lhs_type) == INTEGER_TYPE
+	      && TYPE_MODE (lhs_type) == TYPE_MODE (char_type_node)
+	      && TYPE_PRECISION (lhs_type) == TYPE_PRECISION (char_type_node))
 	    {
 	      if (! handle_char_store (gsi))
 		return false;
@@ -3988,12 +4374,18 @@ class strlen_dom_walker : public dom_walker
 {
 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;
@@ -4005,6 +4397,8 @@ public:
 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)
@@ -4078,8 +4472,16 @@ strlen_dom_walker::before_dom_children (basic_block bb)
 
   /* 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;
@@ -4096,6 +4498,8 @@ strlen_dom_walker::before_dom_children (basic_block bb)
 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);
@@ -4123,7 +4527,10 @@ const pass_data pass_data_strlen =
   "strlen", /* name */
   OPTGROUP_NONE, /* optinfo_flags */
   TV_TREE_STRLEN, /* tv_id */
-  ( PROP_cfg | PROP_ssa ), /* properties_required */
+  /* Normally the pass would require PROP_ssa but because it also
+     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 */
@@ -4132,20 +4539,48 @@ const pass_data pass_data_strlen =
 
 class pass_strlen : public gimple_opt_pass
 {
+  bool do_optimize;
+
 public:
   pass_strlen (gcc::context *ctxt)
-    : gimple_opt_pass (pass_data_strlen, ctxt)
+    : gimple_opt_pass (pass_data_strlen, ctxt),
+    do_optimize ()
   {}
 
   /* opt_pass methods: */
-  virtual bool gate (function *) { return flag_optimize_strlen != 0; }
+
+  opt_pass * clone () { return new pass_strlen (m_ctxt); }
+
+  virtual bool gate (function *);
   virtual unsigned int execute (function *);
 
+  void set_pass_param (unsigned int n, bool param)
+    {
+      gcc_assert (n == 0);
+      do_optimize = param;
+    }
 }; // class pass_strlen
 
+
+bool
+pass_strlen::gate (function *)
+{
+  /* Run the pass iff -Wformat-overflow or -Wformat-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_optimize_strlen > 0
+	   || flag_printf_return_value)
+	  && (optimize > 0) == do_optimize);
+}
+
 unsigned int
 pass_strlen::execute (function *fun)
 {
+  strlen_optimize = do_optimize;
+
   gcc_assert (!strlen_to_stridx);
   if (warn_stringop_overflow || warn_stringop_truncation)
     strlen_to_stridx = new hash_map<tree, stridx_strlenloc> ();
@@ -4155,10 +4590,17 @@ pass_strlen::execute (function *fun)
 
   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 ();
@@ -4179,6 +4621,15 @@ pass_strlen::execute (function *fun)
       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;
 }
 
diff --git a/gcc/tree-ssa-strlen.h b/gcc/tree-ssa-strlen.h
index 0b68465eb2b..32bd32e8445 100644
--- a/gcc/tree-ssa-strlen.h
+++ b/gcc/tree-ssa-strlen.h
@@ -25,4 +25,11 @@ extern bool is_strlen_related_p (tree, tree);
 extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
 extern tree set_strlen_range (tree, 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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]