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)


On 8/9/19 11:07 AM, Jeff Law wrote:
On 7/10/19 5:54 PM, Martin Sebor wrote:
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.
OK.  Just wanted to make sure I understood it's structure at the highest
level.



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.
I wasn't necessarily referring to any of the strlen/sprintf code when I
made that comment.  I was thinking more about DOM and jump threading
where I think I've got extraneous "class" all over the place.  And I
can't recall ever auditing for const-correctness.  Both are probably
worth fixing.



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.
OK.  Thanks for clarifying.  That's probably why we have the someone
unusual gating tests.




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=]
That does sound familiar.  But ISTM in a non-optimized case we could
still just run the late one and get the warning.  It would seem the
problem with that is the late pass is inside the pass_all_optimizations
in passes.def.

We'd probably have to close the pass_all_optimizations, do the sprintf
checking, then open a new pass_all_optimizations_something for the rest
of the pipeline currently under pass_all_optimizations.

Seems out of scope for now, but worth remembering.



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.
It's more about existing conventions and consistency in the codebase.
We've used NULL to refer to "null pointer" for decades.

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.
So I think we can come back to the passes above and add limiters to them
as a distinct patchkit.  And I think we will ultimately want those limiters.

@@ -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.
I probably should have been clearer.  Let's go ahead and put in a prefix
for now, but the better long term plan is to classify the pass, address
const-correctness, initialization points, etc  Each of which should
probably happen independently.  I happen to have a patch which does the
classification, so that would fall to me naturally.

I assume you want me to add the strlen_ prefix back so I did that.

The function used to be called strlen_optimize_call.  You made me
change it to strlen_check_and_optimize_call when I added warnings
to the pass.  I dropped the "strlen_" part in this patch because
the function serves both "passes" (i.e., strlen and sprintf).

FWIW, this is a static function with a single caller just a little
bit later in the file so there's no risk that it will be called
from anywhere else with the wrong expectations.  There is no also
discernible convention for static function names (or any others)
in any pass that I've looked at, so from where I sit it doesn't
matter what this one is called.

@@ -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.
So I'm not really comfortable dropping PROP_ssa.  Basically I can't
convince myself that doing so won't ultimately drop PROP_ssa from the
current function's properties.  Which in turn would turn off certain
sanity checkers in passes.def.

What I think this argues is that you want to register two distinct
passes.  They would differ in their "name" and "properties required"
fields.  You may also be able to clean up the gating with that change as
well.  They obviously share a ton of underlying code.

Okay, I did that.  AFAICS, PROP_ssa controls running verify_ssa()
in tree-ssa.c so it will do more checking than without.

Attached is the updated patch with the two changes.

Martin

+
   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.
Ah, it's just moved from the sprintf pass into strlen.  I see what
happened now.

Still have to review the updated patch, but wanted to close on some of
the issues we were already discussing.

jeff


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

gcc/ChangeLog:

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

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..66b09e558c7 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_warn_printf);
   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);
@@ -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);
       /* 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..2a454df88a1 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/pr81292-2.c b/gcc/testsuite/gcc.dg/pr81292-2.c
index c1c507f982e..252884abca7 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/pr81703.c b/gcc/testsuite/gcc.dg/pr81703.c
index 190f4a833dd..02edf267437 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" "strlen1" } } */
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..24772c14499 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-10.c b/gcc/testsuite/gcc.dg/strlenopt-10.c
index 97167df959f..ce959c34a80 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 "strlen1" } } */
 /* avr has BIGGEST_ALIGNMENT 8, allowing fold_builtin_memory_op
    to expand the memcpy call at the end of fn2.  */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 8 "strlen" { target { ! avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen" { target { avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "\\*q_\[0-9\]* = 32;" 1 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(\[^\n\r\]*, 1\\)" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 8 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" { target { avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "\\*q_\[0-9\]* = 32;" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(\[^\n\r\]*, 1\\)" 1 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-11.c b/gcc/testsuite/gcc.dg/strlenopt-11.c
index f7fa44bde35..abd9faebed6 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 "strlen1" } } */
 /* avr has BIGGEST_ALIGNMENT 8, allowing fold_builtin_memory_op
    to expand the memcpy call at the end of fn1.  */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen" { target { ! avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" { target { avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" { target { avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
 /* Where the memcpy is expanded, the assignemts to elements of l are
    propagated.  */
-/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.0. = " 1 "strlen" { target { ! avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.6. = " 1 "strlen" { target { ! avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.9. = " 1 "strlen" { target { ! avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;" 3 "strlen" { target { avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.0. = " 1 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.6. = " 1 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.9. = " 1 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;" 3 "strlen1" { target { avr-*-* } } } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-13.c b/gcc/testsuite/gcc.dg/strlenopt-13.c
index 3502599b28c..27ecc79c2d9 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 "strlen1" } } */
 /* avr has BIGGEST_ALIGNMENT 8, allowing fold_builtin_memory_op
    to expand the memcpy call at the end of fn1.  */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen" { target { ! avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen" { target { avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
-/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" { target { avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
 /* Where the memcpy is expanded, the assignemts to elements of l are
    propagated.  */
-/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.0. = " 1 "strlen" { target { ! avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.1. = " 1 "strlen" { target { ! avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.5. = " 1 "strlen" { target { ! avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.6. = " 1 "strlen" { target { ! avr-*-* } } } } */
-/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;" 4 "strlen" { target { avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.0. = " 1 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.1. = " 1 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.5. = " 1 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;\[\n\r\]*  l.6. = " 1 "strlen1" { target { ! avr-*-* } } } } */
+/* { dg-final { scan-tree-dump-times "  _\[0-9\]* = strlen \\(\[^\n\r\]*;" 4 "strlen1" { target { avr-*-* } } } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-14g.c b/gcc/testsuite/gcc.dg/strlenopt-14g.c
index 62a83bf8fd7..1368ed3f68e 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-14gf.c b/gcc/testsuite/gcc.dg/strlenopt-14gf.c
index 8b126fcb7ea..f7db2a8a53e 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__mempcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-15.c b/gcc/testsuite/gcc.dg/strlenopt-15.c
index 827ea07b6ea..b72c096ffa9 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-16g.c b/gcc/testsuite/gcc.dg/strlenopt-16g.c
index 0cf8410735a..816cbbce801 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-17g.c b/gcc/testsuite/gcc.dg/strlenopt-17g.c
index 184e530788e..aa86f78ec7f 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "mempcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-18g.c b/gcc/testsuite/gcc.dg/strlenopt-18g.c
index f734675ec40..de692e0767a 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-19.c b/gcc/testsuite/gcc.dg/strlenopt-19.c
index 022ba8b4787..814f51b27f6 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-1f.c b/gcc/testsuite/gcc.dg/strlenopt-1f.c
index 856774de7ad..4e3abd9472d 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-2.c b/gcc/testsuite/gcc.dg/strlenopt-2.c
index fd59a3cd513..b09f7c17e39 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 5 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-20.c b/gcc/testsuite/gcc.dg/strlenopt-20.c
index 7b483eaeac1..79db12bc102 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-21.c b/gcc/testsuite/gcc.dg/strlenopt-21.c
index 05b85a49dde..7924ff30b51 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-22.c b/gcc/testsuite/gcc.dg/strlenopt-22.c
index b4ef772f0e5..2d127b11832 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-22g.c b/gcc/testsuite/gcc.dg/strlenopt-22g.c
index 9c5d020588f..1ecb85eae0c 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 1 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-24.c b/gcc/testsuite/gcc.dg/strlenopt-24.c
index 639501a53e0..275b5602b70 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-25.c b/gcc/testsuite/gcc.dg/strlenopt-25.c
index 89b60e3ebed..faed5bec889 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-26.c b/gcc/testsuite/gcc.dg/strlenopt-26.c
index 6bb0263d315..0385aceaad3 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-27.c b/gcc/testsuite/gcc.dg/strlenopt-27.c
index c539edb821c..e3655930b3e 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-28.c b/gcc/testsuite/gcc.dg/strlenopt-28.c
index 03fb01781bd..6bdbed7e9d0 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-29.c b/gcc/testsuite/gcc.dg/strlenopt-29.c
index fb4b4c9cc71..8922101f549 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-2f.c b/gcc/testsuite/gcc.dg/strlenopt-2f.c
index 1e915dac928..5786f8a1904 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 5 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-3.c b/gcc/testsuite/gcc.dg/strlenopt-3.c
index f17779c0a66..a748f011c80 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
 /* { dg-final { scan-tree-dump-times "return 0" 3 "optimized" } } */
 /* { dg-final { scan-tree-dump-times "return 4" 1 "optimized" } } */
 /* { dg-final { scan-tree-dump-times "return 3" 1 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-30.c b/gcc/testsuite/gcc.dg/strlenopt-30.c
index a85df686ce2..2a3098ba96f 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-31g.c b/gcc/testsuite/gcc.dg/strlenopt-31g.c
index 45cc29c1024..4eff86448f1 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-not "strlen \\(" "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-32.c b/gcc/testsuite/gcc.dg/strlenopt-32.c
index 08eb6bc2b08..4220314fb3f 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-33.c b/gcc/testsuite/gcc.dg/strlenopt-33.c
index 1e1c4dee1f8..4903a91a0a4 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-33g.c b/gcc/testsuite/gcc.dg/strlenopt-33g.c
index 7d24d2bfc32..a814160e234 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-34.c b/gcc/testsuite/gcc.dg/strlenopt-34.c
index c9433c0399e..1979370cb5e 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-35.c b/gcc/testsuite/gcc.dg/strlenopt-35.c
index 03b3e13a8e2..d175fd7ff50 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 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-4.c b/gcc/testsuite/gcc.dg/strlenopt-4.c
index 802e4ca7ce4..1d0a6e615bc 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 3 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 3 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-48.c b/gcc/testsuite/gcc.dg/strlenopt-48.c
index 179edd82a43..c2d0ac66521 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 "strlen1" 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..bbea0e2db72 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 "strlen1" 0 "gimple" } }
    { dg-final { scan-tree-dump-times "len0 = 0;" 1 "gimple" } }
    { dg-final { scan-tree-dump-times "len = 18;" 1 "gimple" } }
    { dg-final { scan-tree-dump-times "lenx = 8;" 1 "gimple" } }
diff --git a/gcc/testsuite/gcc.dg/strlenopt-4g.c b/gcc/testsuite/gcc.dg/strlenopt-4g.c
index 879d5666c90..88dcec647e7 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-4gf.c b/gcc/testsuite/gcc.dg/strlenopt-4gf.c
index 7f261b7d34d..033661ad4a9 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__memcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__strcat_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "__stpcpy_chk \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 5 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-5.c b/gcc/testsuite/gcc.dg/strlenopt-5.c
index a24aea44e8b..5a31322bfa1 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-50.c b/gcc/testsuite/gcc.dg/strlenopt-50.c
index 1d1d36808a9..8e7c9dbd93d 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 "strlen1" 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..22a89385f5e 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 "strlen1" 0 "gimple" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-52.c b/gcc/testsuite/gcc.dg/strlenopt-52.c
index 03e063b435e..97b3da7cd0e 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 "strlen1" 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..489c22b6937 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 "strlen1" 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..d4e57ff6255 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 "strlen1" 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..ea6fb22a2ed 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 "strlen1" 0 "gimple" } }
    { dg-final { scan-tree-dump-times "memcmp" 0 "gimple" } }
    { dg-final { scan-tree-dump-times "strcmp" 0 "gimple" } }
    { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-56.c b/gcc/testsuite/gcc.dg/strlenopt-56.c
index 39a532bf8d4..ffd02f1b6cf 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 "strlen1" 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..dcbe778a27a 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 7 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-61.c b/gcc/testsuite/gcc.dg/strlenopt-61.c
index 4f8e9c053e4..3ddfa2ebc9f 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 "strlen1" "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..ba62c039622 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "\\*r_\[0-9\]* = 0;" 1 "strlen1" } } */
 /* { dg-final { scan-tree-dump-times "return 3;" 1 "optimized" } } */
 /* { dg-final { scan-tree-dump-times "return 0;" 2 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-8.c b/gcc/testsuite/gcc.dg/strlenopt-8.c
index 85c6d38a965..21d55d8a843 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 "strlen1" { xfail non_strict_align } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen1" { target { non_strict_align } } } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen1" { target { ! non_strict_align } } } }  */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-9.c b/gcc/testsuite/gcc.dg/strlenopt-9.c
index e8ff1023d71..df6b594946a 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 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 6 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 1 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen1" } } */
 /* { dg-final { scan-tree-dump-times "return 4;" 1 "optimized" } } */
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-pass.h b/gcc/tree-pass.h
index 1c8df3d0a71..be7603a4c72 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -419,6 +419,7 @@ extern gimple_opt_pass *make_pass_omp_target_link (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_oacc_device_lower (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_omp_device_lower (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_object_sizes (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_warn_printf (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_strlen (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_fold_builtins (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_post_ipa_warn (gcc::context *ctxt);
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 88b6bd7869e..ef3c3736983 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,232 @@ 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
+strlen_check_and_optimize_call (gimple_stmt_iterator *gsi,
+				const vr_values *rvals)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+
+  if (!flag_optimize_strlen
+      || !strlen_optimize
+      || !valid_builtin_call (stmt))
+    {
+      /* When not optimizing we must be checking printf calls which
+	 we do even for user-defined functions when they are declared
+	 with attribute format.  */
+      handle_printf_call (gsi, rvals);
+      return true;
+    }
+
+  tree callee = gimple_call_fndecl (stmt);
+  switch (DECL_FUNCTION_CODE (callee))
+    {
+    case BUILT_IN_STRLEN:
+    case BUILT_IN_STRNLEN:
+      handle_builtin_strlen (gsi);
+      break;
+    case BUILT_IN_STRCHR:
+      handle_builtin_strchr (gsi);
+      break;
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+      handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi);
+      break;
+
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+      handle_builtin_strncat (DECL_FUNCTION_CODE (callee), gsi);
+      break;
+
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      handle_builtin_stxncpy (DECL_FUNCTION_CODE (callee), gsi);
+      break;
+
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+      handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi);
+      break;
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+      handle_builtin_strcat (DECL_FUNCTION_CODE (callee), gsi);
+      break;
+    case BUILT_IN_MALLOC:
+    case BUILT_IN_CALLOC:
+      handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi);
+      break;
+    case BUILT_IN_MEMSET:
+      if (handle_builtin_memset (gsi))
+	return false;
+      break;
+    case BUILT_IN_MEMCMP:
+      if (handle_builtin_memcmp (gsi))
+	return false;
+      break;
+    case BUILT_IN_STRCMP:
+    case BUILT_IN_STRNCMP:
+      if (handle_builtin_string_cmp (gsi))
+	return false;
+      break;
+    default:
+      handle_printf_call (gsi, rvals);
+      break;
+    }
+
+  return true;
+}
+
+/* Handle an assignment statement at *GSI to a LHS of integral type.
+   If GSI's basic block needs clean-up of EH, set *CLEANUP_EH to true.  */
+
+static void
+handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree lhs = gimple_assign_lhs (stmt);
+  tree lhs_type = TREE_TYPE (lhs);
+
+  enum tree_code code = gimple_assign_rhs_code (stmt);
+  if (code == COND_EXPR)
+    {
+      tree cond = gimple_assign_rhs1 (stmt);
+      enum tree_code cond_code = TREE_CODE (cond);
+
+      if (cond_code == EQ_EXPR || cond_code == NE_EXPR)
+	fold_strstr_to_strncmp (TREE_OPERAND (cond, 0),
+				TREE_OPERAND (cond, 1), stmt);
+    }
+  else if (code == EQ_EXPR || code == NE_EXPR)
+    fold_strstr_to_strncmp (gimple_assign_rhs1 (stmt),
+			    gimple_assign_rhs2 (stmt), stmt);
+  else if (gimple_assign_load_p (stmt)
+	   && TREE_CODE (lhs_type) == INTEGER_TYPE
+	   && TYPE_MODE (lhs_type) == TYPE_MODE (char_type_node)
+	   && (TYPE_PRECISION (lhs_type)
+	       == TYPE_PRECISION (char_type_node))
+	   && !gimple_has_volatile_ops (stmt))
+    {
+      tree off = integer_zero_node;
+      unsigned HOST_WIDE_INT coff = 0;
+      int idx = 0;
+      tree rhs1 = gimple_assign_rhs1 (stmt);
+      if (code == MEM_REF)
+	{
+	  idx = get_stridx (TREE_OPERAND (rhs1, 0));
+	  if (idx > 0)
+	    {
+	      strinfo *si = get_strinfo (idx);
+	      if (si
+		  && si->nonzero_chars
+		  && TREE_CODE (si->nonzero_chars) == INTEGER_CST
+		  && (wi::to_widest (si->nonzero_chars)
+		      >= wi::to_widest (off)))
+		off = TREE_OPERAND (rhs1, 1);
+	      else
+		/* This case is not useful.  See if get_addr_stridx
+		   returns something usable.  */
+		idx = 0;
+	    }
+	}
+      if (idx <= 0)
+	idx = get_addr_stridx (rhs1, NULL_TREE, &coff);
+      if (idx > 0)
+	{
+	  strinfo *si = get_strinfo (idx);
+	  if (si
+	      && si->nonzero_chars
+	      && TREE_CODE (si->nonzero_chars) == INTEGER_CST)
+	    {
+	      widest_int w1 = wi::to_widest (si->nonzero_chars);
+	      widest_int w2 = wi::to_widest (off) + coff;
+	      if (w1 == w2
+		  && si->full_string_p)
+		{
+		  if (dump_file && (dump_flags & TDF_DETAILS) != 0)
+		    {
+		      fprintf (dump_file, "Optimizing: ");
+		      print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
+		    }
+
+		  /* Reading the final '\0' character.  */
+		  tree zero = build_int_cst (lhs_type, 0);
+		  gimple_set_vuse (stmt, NULL_TREE);
+		  gimple_assign_set_rhs_from_tree (gsi, zero);
+		  *cleanup_eh
+		    |= maybe_clean_or_replace_eh_stmt (stmt,
+						       gsi_stmt (*gsi));
+		  stmt = gsi_stmt (*gsi);
+		  update_stmt (stmt);
+
+		  if (dump_file && (dump_flags & TDF_DETAILS) != 0)
+		    {
+		      fprintf (dump_file, "into: ");
+		      print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
+		    }
+		}
+	      else if (w1 > w2)
+		{
+		  /* Reading a character before the final '\0'
+		     character.  Just set the value range to ~[0, 0]
+		     if we don't have anything better.  */
+		  wide_int min, max;
+		  signop sign = TYPE_SIGN (lhs_type);
+		  int prec = TYPE_PRECISION (lhs_type);
+		  value_range_kind vr = get_range_info (lhs, &min, &max);
+		  if (vr == VR_VARYING
+		      || (vr == VR_RANGE
+			  && min == wi::min_value (prec, sign)
+			  && max == wi::max_value (prec, sign)))
+		    set_range_info (lhs, VR_ANTI_RANGE,
+				    wi::zero (prec), wi::zero (prec));
+		}
+	    }
+	}
+    }
+
+  if (strlen_to_stridx)
+    {
+      tree rhs1 = gimple_assign_rhs1 (stmt);
+      if (stridx_strlenloc *ps = strlen_to_stridx->get (rhs1))
+	strlen_to_stridx->put (lhs, stridx_strlenloc (*ps));
+    }
+}
+
 /* Attempt to check for validity of the performed access a single statement
    at *GSI using string length knowledge, and to optimize it.
    If the given basic block needs clean-up of EH, CLEANUP_EH is set to
    true.  */
 
 static bool
-strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh)
+check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
+			 const vr_values *rvals)
 {
   gimple *stmt = gsi_stmt (*gsi);
 
   if (is_gimple_call (stmt))
     {
-      tree callee = gimple_call_fndecl (stmt);
-      if (valid_builtin_call (stmt))
-	switch (DECL_FUNCTION_CODE (callee))
-	  {
-	  case BUILT_IN_STRLEN:
-	  case BUILT_IN_STRNLEN:
-	    handle_builtin_strlen (gsi);
-	    break;
-	  case BUILT_IN_STRCHR:
-	    handle_builtin_strchr (gsi);
-	    break;
-	  case BUILT_IN_STRCPY:
-	  case BUILT_IN_STRCPY_CHK:
-	  case BUILT_IN_STPCPY:
-	  case BUILT_IN_STPCPY_CHK:
-	    handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi);
-	    break;
-
-	  case BUILT_IN_STRNCAT:
-	  case BUILT_IN_STRNCAT_CHK:
-	    handle_builtin_strncat (DECL_FUNCTION_CODE (callee), gsi);
-	    break;
-
-	  case BUILT_IN_STPNCPY:
-	  case BUILT_IN_STPNCPY_CHK:
-	  case BUILT_IN_STRNCPY:
-	  case BUILT_IN_STRNCPY_CHK:
-	    handle_builtin_stxncpy (DECL_FUNCTION_CODE (callee), gsi);
-	    break;
-
-	  case BUILT_IN_MEMCPY:
-	  case BUILT_IN_MEMCPY_CHK:
-	  case BUILT_IN_MEMPCPY:
-	  case BUILT_IN_MEMPCPY_CHK:
-	    handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi);
-	    break;
-	  case BUILT_IN_STRCAT:
-	  case BUILT_IN_STRCAT_CHK:
-	    handle_builtin_strcat (DECL_FUNCTION_CODE (callee), gsi);
-	    break;
-	  case BUILT_IN_MALLOC:
-	  case BUILT_IN_CALLOC:
-	    handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi);
-	    break;
-	  case BUILT_IN_MEMSET:
-	    if (handle_builtin_memset (gsi))
-	      return false;
-	    break;
-	  case BUILT_IN_MEMCMP:
-	    if (handle_builtin_memcmp (gsi))
-	      return false;
-	    break;
-	  case BUILT_IN_STRCMP:
-	  case BUILT_IN_STRNCMP:
-	    if (handle_builtin_string_cmp (gsi))
-	      return false;
-	    break;
-	  default:
-	    break;
-	  }
+      if (!strlen_check_and_optimize_call (gsi, rvals))
+	return false;
     }
+  else if (!flag_optimize_strlen || !strlen_optimize)
+    return true;
   else if (is_gimple_assign (stmt) && !gimple_clobber_p (stmt))
     {
+      /* Handle non-clobbering assignment.  */
       tree lhs = gimple_assign_lhs (stmt);
+      tree lhs_type = TREE_TYPE (lhs);
 
-      if (TREE_CODE (lhs) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (lhs)))
+      if (TREE_CODE (lhs) == SSA_NAME && POINTER_TYPE_P (lhs_type))
 	{
 	  if (gimple_assign_single_p (stmt)
 	      || (gimple_assign_cast_p (stmt)
@@ -3796,124 +4291,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 +4375,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 +4398,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 +4473,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 +4499,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);
@@ -4113,39 +4518,13 @@ strlen_dom_walker::after_dom_children (basic_block bb)
     }
 }
 
-/* Main entry point.  */
-
 namespace {
 
-const pass_data pass_data_strlen =
+static unsigned int
+printf_strlen_execute (function *fun, bool warn_only)
 {
-  GIMPLE_PASS, /* type */
-  "strlen", /* name */
-  OPTGROUP_NONE, /* optinfo_flags */
-  TV_TREE_STRLEN, /* tv_id */
-  ( PROP_cfg | PROP_ssa ), /* properties_required */
-  0, /* properties_provided */
-  0, /* properties_destroyed */
-  0, /* todo_flags_start */
-  0, /* todo_flags_finish */
-};
-
-class pass_strlen : public gimple_opt_pass
-{
-public:
-  pass_strlen (gcc::context *ctxt)
-    : gimple_opt_pass (pass_data_strlen, ctxt)
-  {}
+  strlen_optimize = !warn_only;
 
-  /* opt_pass methods: */
-  virtual bool gate (function *) { return flag_optimize_strlen != 0; }
-  virtual unsigned int execute (function *);
-
-}; // class pass_strlen
-
-unsigned int
-pass_strlen::execute (function *fun)
-{
   gcc_assert (!strlen_to_stridx);
   if (warn_stringop_overflow || warn_stringop_truncation)
     strlen_to_stridx = new hash_map<tree, stridx_strlenloc> ();
@@ -4155,10 +4534,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,11 +4565,113 @@ 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;
 }
 
+/* This file defines two passes: one for warnings that runs only when
+   optimization is disabled, and another that implements optimizations
+   and also issues warnings.  */
+
+const pass_data pass_data_warn_printf =
+{
+  GIMPLE_PASS, /* type */
+  "warn-printf", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  /* Normally an optimization pass would require PROP_ssa but because
+     this pass runs early, with no optimization, to do sprintf format
+     checking, it only requires PROP_cfg.  */
+  PROP_cfg, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_warn_printf : public gimple_opt_pass
+{
+public:
+  pass_warn_printf (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_warn_printf, ctxt)
+  {}
+
+  virtual bool gate (function *);
+  virtual unsigned int execute (function *fun)
+  {
+    return printf_strlen_execute (fun, true);
+  }
+};
+
+
+/* Return true to run the warning pass only when not optimizing and
+   iff either -Wformat-overflow or -Wformat-truncation is specified.  */
+
+bool
+pass_warn_printf::gate (function *)
+{
+  return !optimize && (warn_format_overflow > 0 || warn_format_trunc > 0);
+}
+
+const pass_data pass_data_strlen =
+{
+  GIMPLE_PASS, /* type */
+  "strlen", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_STRLEN, /* tv_id */
+  PROP_cfg | PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_strlen : public gimple_opt_pass
+{
+public:
+  pass_strlen (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_strlen, ctxt)
+  {}
+
+  opt_pass * clone () { return new pass_strlen (m_ctxt); }
+
+  virtual bool gate (function *);
+  virtual unsigned int execute (function *fun)
+  {
+    return printf_strlen_execute (fun, false);
+  }
+};
+
+/* Return true to run the pass only when the sprintf and/or strlen
+   optimizations are enabled and -Wformat-overflow or -Wformat-truncation
+   are specified.  */
+
+bool
+pass_strlen::gate (function *)
+{
+  return ((warn_format_overflow > 0
+	   || warn_format_trunc > 0
+	   || flag_optimize_strlen > 0
+	   || flag_printf_return_value)
+	  && optimize > 0);
+}
+
 } // anon namespace
 
+gimple_opt_pass *
+make_pass_warn_printf (gcc::context *ctxt)
+{
+  return new pass_warn_printf (ctxt);
+}
+
 gimple_opt_pass *
 make_pass_strlen (gcc::context *ctxt)
 {
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]