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 1/4] introduce struct strlen_data_t into gimple-fold


On 10/10/18 1:39 PM, Jeff Law wrote:
On 10/2/18 10:37 AM, Martin Sebor wrote:
[1/4] - Introduce struct strlen_data_t into gimple-fold

This patch introduces a new data structure to reduce the number
of arguments and overloads of the get_range_strlen API.  One of
the goals of this change is to make the API safer to use for
optimization (which looks for "permissive" results to account
even for some undefined uses) vs warnings (which relies on
conservative results to avoid false positives).  The patch also
adds provides explicit arguments to get_range_strlen and adds
descriptive comments to make the affected code easier to follow.
Beyond making use of the new data structure the patch makes no
observable changes.

The changes to gcc/testsuite/gcc.dg/strlenopt-51.c fix a few
typos with no functional effect and tweak the helper macro
used by the test to make debugging slightly easier.

gcc-99999-1.diff

[1/4] - Introduce struct strlen_data_t into gimple-fold.

gcc/ChangeLog:

	* builtins.c (check_access): Document argumens to a function call.
	(check_strncat_sizes): Same.
	(expand_builtin_strncat): Same.
	* calls.c (maybe_warn_nonstring_arg): Same.
	* gimple-fold.h (struct strlen_data_t): New type.
	(get_range_strlen): New overload.
	* gimple-fold.c (struct strlen_data_t): New type.
	(get_range_strlen): Change declaration to take strlen_data_t*
	argument instead of length, flexp, eltsize, and nonstr.
	Adjust to use strlen_data_t members instead of other arguments.
	Also set strlen_data_t::maxsize to the same value as maxlen.
	(extern get_range_strlen): Define new overload.
	(get_maxval_strlen): Adjust to use strlen_data_t.
	* gimple-ssa-sprintf.c (get_string_length): Same.

gcc/testsuite/ChangeLog:
	gcc.dg/strlenopt-51.c: Add counter to macros and fix typos.

diff --git a/gcc/calls.c b/gcc/calls.c
index e9660b6..11f00ad 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1582,7 +1585,8 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
  	  {
  	    tree arg = CALL_EXPR_ARG (exp, argno);
g 	    if (!get_attr_nonstring_decl (arg))
-	      get_range_strlen (arg, lenrng);
+	      get_range_strlen (arg, lenrng, /* eltsize = */ 1,
+				/* strict = */ false);
  	  }
        }
        /* Fall through.  */
As Bernd noted, something clearly went wrong here with the patch the "g"
at the beginning of the line.  Hand-edited patch perhaps?  Regardless I
assume you'll fix this.


diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 1e84722..8f71e9c 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -1262,11 +1262,13 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
  }
-/* Obtain the minimum and maximum string length or minimum and maximum
-   value of ARG in LENGTH[0] and LENGTH[1], respectively.
+/* Try to obtain the range of the lengths of the string(s) referenced
+   by ARG, or the size of the largest array ARG referes to if the range
+   of lengths cannot be determined, and store all in *PDATA.
     If ARG is an SSA name variable, follow its use-def chains.  When
-   TYPE == 0, if LENGTH[1] is not equal to the length we determine or
-   if we are unable to determine the length or value, return false.
+   TYPE == 0, then if PDATA->MAXLEN is not equal to the determined
+   length or if we are unable to determine the length or value, return
+   false.
     VISITED is a bitmap of visited variables.
     TYPE is 0 if string length should be obtained, 1 for maximum string
     length and 2 for maximum value ARG can have.
So can we clarify the return value?  What is the return value for types
other than 0?  I struggled with parsing that before this patch :-)

I added a comment.

  static bool
-get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
-		  int fuzzy, bool *flexp, unsigned eltsize, tree *nonstr)
+get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
+		  strlen_data_t *pdata)
So ISTM we should keep ELTSIZE as distinct parameter as its strictly an
input.  That in turn allows *PDATA to be strictly filled in as an output.

Done (but see below).

diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h
index 26e2727..0d523e7 100644
--- a/gcc/gimple-fold.h
+++ b/gcc/gimple-fold.h
@@ -25,6 +25,53 @@ along with GCC; see the file COPYING3.  If not see
  extern tree create_tmp_reg_or_ssa_name (tree, gimple *stmt = NULL);
  extern tree canonicalize_constructor_val (tree, tree);
  extern tree get_symbol_constant_value (tree);
+
+struct strlen_data_t
+{
+  /* [MIN, MAXSIZE, MAXLEN] is a range describing the length of
+     a string of possibly unknown length.  For a string of known
+     length the range is a constant where MIN == MAXSIZE == MAXLEN
+     holds.
+     For other strings, MIN is the length of the shortest string that
+     can be stored in the referenced object, i.e., MIN == 0.  MAXSIZE
+     is the size of the largest array referenced by the expression.
+     MAXLEN is the length of the longest sequence of non-zero bytes
+     in memory reference by the expression.  For such strings,
+     MIN <= MAXSIZE <= MAXLEN holds.  For example, given:
Hmm, the description here is a little confusing WRT MAXSIZE vs MAXLEN.
I think the distinction we're trying to make is that MAXSIZE is the
maximum respecting subobject boundaries and that MAXLEN is the maximum
without respecting subobject boundaries.

This wasn't the final version of the comment.  Patch 3/4 updated
it again to more closely reflect the relationship of the members,
though it didn't change the names.

If so, then I think we need
to look for a better name than MAXSIZE and MAXLEN.

I find these names quite fitting and I'm not sure what might work
better.  I renamed MAXSIZE to MAXBOUND but nothing comes to mind
as a replacement for MAXLEN.  Please suggest something you think
is better.


+  /* When non-null, NONSTR refers to the declaration known to store
+     an unterminated constant character array, as in:
+     const char s[] = { 'a', 'b', 'c' };
+     It is used to diagnose uses of such arrays in functions such as
+     strlen() that expect a nul-terminated string as an argument.  */
+  tree nonstr;
So rather than NONSTR, DECL may make more sense -- if for no other
reason than you don't have to think in terms of "not a string".

Done, but I think DECL is a poor choice for the reasons below.

The field is only set when the thing the object refers to is
a character array that is not a string.  It identifies the first
array the expression refers to that's not a terminated string
(there could be multiple).  I can't think of anything else one
might want to think of it as than "a declaration of an array
that is not a string."

As a name, DECL is generic and used all over the place for any
sort of a declaration so it's not a good choice also for that
reason.  It's only marginally more descriptive that the pervasive
NODE or T, but just as useless to grep for (which I have been
relying on when working with this patch).

I have been using the name NONSTR in all contexts where
I introduced the unterminated array handling, so renaming
the member to DECL makes this scheme inconsistent.

+  /* ELTSIZE is set to 1 for single byte character strings, and 2 or 4
+     for wide characer strings.  */
+  unsigned eltsize;
Bernd's suggestion that we separate the input vs output paramters may be
a reasonable one -- I think this is the only in-parameter you're passing
with the structure, right?  And everything else is a pure output?  If so
we may be better off continuing to pass the element size as a separate
parameter.

I changed it in the updated patch.  I had chosen to make it
a member to reduce the number of arguments to these functions and
in anticipation of having them update it before returning if they
discover that the actual element size doesn't match the expected
size, as in:

  printf ("%ls", "narrow string");

Similarly to what I proposed here:
  https://gcc.gnu.org/ml/gcc-patches/2018-08/msg01321.html

I don't see what has been gained by making it an argument again.

+  /* FLEXARRAY is true if the range of the string lengths has been
+     obtained from the upper bound of an array at the end of a struct.
+     Such an array may hold a string that's longer than its upper bound
+     due to it being used as a poor-man's flexible array member.  */
+  bool flexarray;
+
+  /* Set ELTSIZE and value-initialize all other members.  */
+  strlen_data_t (unsigned eltbytes)
+    : minlen (), maxlen (), maxsize (), nonstr (), eltsize (eltbytes),
+      flexarray () { }
I think if you pull ELTSIZE out and pass it as a distinct parameter,
then you don't need a ctor and you can have a POD.  You can then
initialize with memset rather than having to individually initialize
each field -- meaning it's easier to add more output fields in the future.

Without ELTSIZE neither a ctor nor memset is necessary for
initialization.  This works too and is the preferred style
in C++ 98:

  c_strlen_data data = { };

I made this change but my preference would have been to keep
the ctor and avoid relying on explicit initialization (especially
using memset).  It avoids the risk of forgetting to initialize
the object and makes object definitions more concise.  That's
what C++ ctors are for.

(Strictly speaking, using memset to zero out a struct with pointer
or floating members isn't the same thing since null pointers and
float zeros need not be represented as all bits zero.)


I don't think any of the suggestions above change the behavior of the
patch.  Let's hold off committing though (I assume you've got a GIT
topic branch where you can make these changes and update the subsequent
patches independent of everything else...)

I do but I don't know how to make these changes without having
to resolve all the conflicts with the intervening changes on
trunk at each step so I have just a single patch now that resolves
the conflicts with trunk committed since I started this work and
that makes the changes you requested above.  The majority of
the changes in the patch are just minor adjustments (the meat
of the change is in gimple-fold.c; all the rest is just minor
adjustments) so hopefully this won't be a problem.

Martin
gcc/ChangeLog:

	* builtins.c (unterminated_array): Rename a local variable for
	consistency.  Use aggregate initialization.
	(c_strlen): Same.
	(expand_builtin_strnlen): Same.
	(check_access): Same.
	(expand_builtin_stpcpy_1): Same.
	(check_strncat_sizes): Same.  Remove locals.
	(expand_builtin_strncat): Use c_strlen_data.
	(fold_builtin_strlen): Rename a local variable for consistency.
	Use aggregate initialization.
	* builtins.h (c_strlen_data):  Add/rename members.
	* calls.c (maybe_warn_nonstring_arg): Use c_strlen_data.
	* gimple-fold.c (strlen_range_kind): New enum.
	(get_range_strlen_tree): New helper.
	(get_range_strlen): Move tree code into get_range_strlen_tree.
	Replace type and fuzzy arguments with enum strlen_range_kind.
	Avoid optimizing ranges based on type, optimize on decl size intead.
	(get_range_strlen): New extern overload.
	(get_range_strlen): Use new overload above.
	(get_maxval_strlen): Declare static.  Assert preconditions.
	Use new overload above.
	(gimple_fold_builtin_strcpy): Adjust to pass enum.
	(gimple_fold_builtin_strncpy): Same.
	(gimple_fold_builtin_strcat): Same.
	(gimple_fold_builtin_fputs): Same.
	(gimple_fold_builtin_memory_chk): Same.
	(gimple_fold_builtin_stxcpy_chk): Same.
	(gimple_fold_builtin_stxncpy_chk): Same.
	(gimple_fold_builtin_snprintf_chk): Same.
	(gimple_fold_builtin_sprintf): Same.
	(gimple_fold_builtin_snprintf): Same.
	(gimple_fold_builtin_strlen): Simplify.  Call set_strlen_range.
	* gimple-fold.h (get_range_strlen): Replace arguments.
	(get_maxval_strlen): Remove unused declaration.
	* gimple-ssa-sprintf.c (get_string_length): Rename a local variable
	for consistency.  Use aggregate initialization.   Call new overload
	of get_range_strlen.  Adjust max, likely, and unlikely counters for
	strings of unbounded lengths.
	(format_string): Set unlikely maximum under specific conditions.
	* tree-ssa-strlen.c (set_strlen_range): New function.
	(maybe_set_strlen_range): Call it.  Make static.
	(maybe_diag_stxncpy_trunc): Use c_strlen_data and new get_range_strlen
	overload.  Adjust.
	* tree-ssa-strlen.h (set_strlen_range): Declare/

gcc/testsuite/ChangeLog:

	* g++.dg/init/strlen.C: New test.
	* gcc.c-torture/execute/strlen-5.c: New test.
	* gcc.c-torture/execute/strlen-6.c: New test.
	* gcc.c-torture/execute/strlen-7.c: New test.
	* gcc.dg/strlenopt-36.c: Remove tests for an overly aggressive
	optimization.
	* gcc.dg/strlenopt-40.c: Adjust tests to reflect a relaxed
	optimization, remove others for an overly aggressive optimization.
	* gcc.dg/strlenopt-45.c: Same.
	* gcc.dg/strlenopt-48.c: Adjust tests to reflect a relaxed
	optimization.
	* gcc.dg/strlenopt-51.c: Same.
	* gcc.dg/strlenopt-59.c: New test.
	* gcc.dg/tree-ssa/builtin-snprintf-4.c: New test.
	* gcc.dg/tree-ssa/pr79376.c: Comment out tests for disabled strlen
	range optimization.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index ebde2db..b54d6ef 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -574,26 +574,25 @@ unterminated_array (tree exp, tree *size /* = NULL */, bool *exact /* = NULL */)
 {
   /* C_STRLEN will return NULL and set DECL in the info
      structure if EXP references a unterminated array.  */
-  c_strlen_data data;
-  memset (&data, 0, sizeof (c_strlen_data));
-  tree len = c_strlen (exp, 1, &data);
-  if (len == NULL_TREE && data.len && data.decl)
+  c_strlen_data lendata = { };
+  tree len = c_strlen (exp, 1, &lendata);
+  if (len == NULL_TREE && lendata.minlen && lendata.decl)
      {
        if (size)
 	{
-	  len = data.len;
-	  if (data.off)
+	  len = lendata.minlen;
+	  if (lendata.off)
 	    {
-	      /* Constant offsets are already accounted for in data.len, but
-		 not in a SSA_NAME + CST expression.  */
-	      if (TREE_CODE (data.off) == INTEGER_CST)
+	      /* Constant offsets are already accounted for in LENDATA.MINLEN,
+		 but not in a SSA_NAME + CST expression.  */
+	      if (TREE_CODE (lendata.off) == INTEGER_CST)
 		*exact = true;
-	      else if (TREE_CODE (data.off) == PLUS_EXPR
-		       && TREE_CODE (TREE_OPERAND (data.off, 1)) == INTEGER_CST)
+	      else if (TREE_CODE (lendata.off) == PLUS_EXPR
+		       && TREE_CODE (TREE_OPERAND (lendata.off, 1)) == INTEGER_CST)
 		{
 		  /* Subtract the offset from the size of the array.  */
 		  *exact = false;
-		  tree temp = TREE_OPERAND (data.off, 1);
+		  tree temp = TREE_OPERAND (lendata.off, 1);
 		  temp = fold_convert (ssizetype, temp);
 		  len = fold_build2 (MINUS_EXPR, ssizetype, len, temp);
 		}
@@ -605,7 +604,7 @@ unterminated_array (tree exp, tree *size /* = NULL */, bool *exact /* = NULL */)
 
 	  *size = len;
 	}
-       return data.decl;
+       return lendata.decl;
      }
 
   return NULL_TREE;
@@ -646,8 +645,7 @@ c_strlen (tree src, int only_value, c_strlen_data *data, unsigned eltsize)
   /* If we were not passed a DATA pointer, then get one to a local
      structure.  That avoids having to check DATA for NULL before
      each time we want to use it.  */
-  c_strlen_data local_strlen_data;
-  memset (&local_strlen_data, 0, sizeof (c_strlen_data));
+  c_strlen_data local_strlen_data = { };
   if (!data)
     data = &local_strlen_data;
 
@@ -721,7 +719,7 @@ c_strlen (tree src, int only_value, c_strlen_data *data, unsigned eltsize)
 	{
 	  data->decl = decl;
 	  data->off = byteoff;
-	  data->len = ssize_int (len);
+	  data->minlen = ssize_int (len);
 	  return NULL_TREE;
 	}
 
@@ -795,7 +793,7 @@ c_strlen (tree src, int only_value, c_strlen_data *data, unsigned eltsize)
     {
       data->decl = decl;
       data->off = byteoff;
-      data->len = ssize_int (len);
+      data->minlen = ssize_int (len);
       return NULL_TREE;
     }
 
@@ -3079,9 +3077,8 @@ expand_builtin_strnlen (tree exp, rtx target, machine_mode target_mode)
 
   /* FIXME: Change c_strlen() to return sizetype instead of ssizetype
      so these conversions aren't necessary.  */
-  c_strlen_data data;
-  memset (&data, 0, sizeof (c_strlen_data));
-  tree len = c_strlen (src, 0, &data, 1);
+  c_strlen_data lendata = { };
+  tree len = c_strlen (src, 0, &lendata, 1);
   if (len)
     len = fold_convert_loc (loc, TREE_TYPE (bound), len);
 
@@ -3103,12 +3100,12 @@ expand_builtin_strnlen (tree exp, rtx target, machine_mode target_mode)
 	       strnlen (&a[i], sizeof a)
 	     where the value of i is unknown.  Unless i's value is
 	     zero, the call is unsafe because the bound is greater. */
-	  data.decl = unterminated_array (src, &len, &exact);
-	  if (!data.decl)
+	  lendata.decl = unterminated_array (src, &len, &exact);
+	  if (!lendata.decl)
 	    return NULL_RTX;
 	}
 
-      if (data.decl
+      if (lendata.decl
 	  && !TREE_NO_WARNING (exp)
 	  && ((tree_int_cst_lt (len, bound))
 	      || !exact))
@@ -3124,7 +3121,7 @@ expand_builtin_strnlen (tree exp, rtx target, machine_mode target_mode)
 			       "of at most %E of unterminated array"),
 			  exp, func, bound, len))
 	    {
-	      inform (DECL_SOURCE_LOCATION (data.decl),
+	      inform (DECL_SOURCE_LOCATION (lendata.decl),
 		      "referenced argument declared here");
 	      TREE_NO_WARNING (exp) = true;
 	      return NULL_RTX;
@@ -3157,12 +3154,12 @@ expand_builtin_strnlen (tree exp, rtx target, machine_mode target_mode)
   bool exact = true;
   if (!len || TREE_CODE (len) != INTEGER_CST)
     {
-      data.decl = unterminated_array (src, &len, &exact);
-      if (!data.decl)
+      lendata.decl = unterminated_array (src, &len, &exact);
+      if (!lendata.decl)
 	return NULL_RTX;
     }
 
-  if (data.decl
+  if (lendata.decl
       && !TREE_NO_WARNING (exp)
       && (wi::ltu_p (wi::to_wide (len), min)
 	  || !exact))
@@ -3178,13 +3175,13 @@ expand_builtin_strnlen (tree exp, rtx target, machine_mode target_mode)
 			   "the size of at most %E of unterminated array"),
 		      exp, func, min.to_uhwi (), max.to_uhwi (), len))
 	{
-	  inform (DECL_SOURCE_LOCATION (data.decl),
+	  inform (DECL_SOURCE_LOCATION (lendata.decl),
 		  "referenced argument declared here");
 	  TREE_NO_WARNING (exp) = true;
 	}
     }
 
-  if (data.decl)
+  if (lendata.decl)
     return NULL_RTX;
 
   if (wi::gtu_p (min, wi::to_wide (len)))
@@ -3338,7 +3335,10 @@ check_access (tree exp, tree, tree, tree dstwrite,
 	     the upper bound given by MAXREAD add one to it for
 	     the terminating nul.  Otherwise, set it to one for
 	     the same reason, or to MAXREAD as appropriate.  */
-	  get_range_strlen (srcstr, range);
+	  c_strlen_data lendata = { };
+	  get_range_strlen (srcstr, &lendata, /* eltsize = */ 1);
+	  range[0] = lendata.minlen;
+	  range[1] = lendata.maxbound;
 	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
 	    {
 	      if (maxread && tree_int_cst_le (maxread, range[0]))
@@ -4087,14 +4087,13 @@ expand_builtin_stpcpy_1 (tree exp, rtx target, machine_mode mode)
 	 compile-time, not an expression containing a string.  This is
 	 because the latter will potentially produce pessimized code
 	 when used to produce the return value.  */
-      c_strlen_data data;
-      memset (&data, 0, sizeof (c_strlen_data));
+      c_strlen_data lendata = { };
       if (!c_getstr (src, NULL)
-	  || !(len = c_strlen (src, 0, &data, 1)))
+	  || !(len = c_strlen (src, 0, &lendata, 1)))
 	return expand_movstr (dst, src, target, /*endp=*/2);
 
-      if (data.decl && !TREE_NO_WARNING (exp))
-	warn_string_no_nul (EXPR_LOCATION (exp), "stpcpy", src, data.decl);
+      if (lendata.decl && !TREE_NO_WARNING (exp))
+	warn_string_no_nul (EXPR_LOCATION (exp), "stpcpy", src, lendata.decl);
 
       lenp1 = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1));
       ret = expand_builtin_mempcpy_args (dst, src, lenp1,
@@ -4210,8 +4209,8 @@ check_strncat_sizes (tree exp, tree objsize)
 
   /* Try to determine the range of lengths that the source expression
      refers to.  */
-  tree lenrange[2];
-  get_range_strlen (src, lenrange);
+  c_strlen_data lendata = { };
+  get_range_strlen (src, &lendata, /* eltsize = */ 1);
 
   /* Try to verify that the destination is big enough for the shortest
      string.  */
@@ -4225,8 +4224,8 @@ check_strncat_sizes (tree exp, tree objsize)
     }
 
   /* Add one for the terminating nul.  */
-  tree srclen = (lenrange[0]
-		 ? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+  tree srclen = (lendata.minlen
+		 ? fold_build2 (PLUS_EXPR, size_type_node, lendata.minlen,
 				size_one_node)
 		 : NULL_TREE);
 
@@ -4278,12 +4277,15 @@ expand_builtin_strncat (tree exp, rtx)
   tree slen = c_strlen (src, 1);
 
   /* Try to determine the range of lengths that the source expression
-     refers to.  */
-  tree lenrange[2];
-  if (slen)
-    lenrange[0] = lenrange[1] = slen;
-  else
-    get_range_strlen (src, lenrange);
+     refers to.  Since the lengths are only used for warning and not
+     for code generation disable strict mode below.  */
+  tree maxlen = slen;
+  if (!maxlen)
+    {
+      c_strlen_data lendata = { };
+      get_range_strlen (src, &lendata, /* eltsize = */ 1);
+      maxlen = lendata.maxbound;
+    }
 
   /* Try to verify that the destination is big enough for the shortest
      string.  First try to determine the size of the destination object
@@ -4291,8 +4293,8 @@ expand_builtin_strncat (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   /* Add one for the terminating nul.  */
-  tree srclen = (lenrange[0]
-		 ? fold_build2 (PLUS_EXPR, size_type_node, lenrange[0],
+  tree srclen = (maxlen
+		 ? fold_build2 (PLUS_EXPR, size_type_node, maxlen,
 				size_one_node)
 		 : NULL_TREE);
 
@@ -8567,23 +8569,22 @@ fold_builtin_strlen (location_t loc, tree type, tree arg)
     return NULL_TREE;
   else
     {
-      c_strlen_data data;
-      memset (&data, 0, sizeof (c_strlen_data));
-      tree len = c_strlen (arg, 0, &data);
+      c_strlen_data lendata = { };
+      tree len = c_strlen (arg, 0, &lendata);
 
       if (len)
 	return fold_convert_loc (loc, type, len);
 
-      if (!data.decl)
-	c_strlen (arg, 1, &data);
+      if (!lendata.decl)
+	c_strlen (arg, 1, &lendata);
 
-      if (data.decl)
+      if (lendata.decl)
 	{
 	  if (EXPR_HAS_LOCATION (arg))
 	    loc = EXPR_LOCATION (arg);
 	  else if (loc == UNKNOWN_LOCATION)
 	    loc = input_location;
-	  warn_string_no_nul (loc, "strlen", arg, data.decl);
+	  warn_string_no_nul (loc, "strlen", arg, lendata.decl);
 	}
 
       return NULL_TREE;
diff --git a/gcc/builtins.h b/gcc/builtins.h
index cf4f9b1..472a86d 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -57,10 +57,48 @@ extern bool get_pointer_alignment_1 (tree, unsigned int *,
 				     unsigned HOST_WIDE_INT *);
 extern unsigned int get_pointer_alignment (tree);
 extern unsigned string_length (const void*, unsigned, unsigned);
+
 struct c_strlen_data
 {
+  /* [MINLEN, MAXBOUND, MAXLEN] is a range describing the length of
+     one or more strings of possibly unknown length.  For a single
+     string of known length the range is a constant where
+     MINLEN == MAXBOUND == MAXLEN holds.
+     For other strings, MINLEN is the length of the shortest known
+     string.  MAXBOUND is the length of a string that could be stored
+     in the largest array referenced by the expression.  MAXLEN is
+     the length of the longest sequence of non-zero bytes
+     in an object referenced by the expression.  For such strings,
+     MINLEN <= MAXBOUND <= MAXLEN holds.  For example, given:
+       struct A { char a[7], b[]; };
+       extern struct A *p;
+       n = strlen (p->a);
+     the computed range will be [0, 6, ALL_ONES].
+     However, for a conditional expression involving a string
+     of known length and an array of unknown bound such as
+       n = strlen (i ? p->b : "123");
+     the range will be [3, 3, ALL_ONES].
+     MINLEN != 0 && MAXLEN == ALL_ONES indicates that MINLEN is
+     the length of the shortest known string and implies that
+     the shortest possible string referenced by the expression may
+     actually be the empty string.  This distinction is useful for
+     diagnostics.  get_range_strlen() return value distinguishes
+     between these two cases.
+     As the tighter (and more optimistic) bound, MAXBOUND is suitable
+     for diagnostics but not for optimization.
+     As the more conservative bound, MAXLEN is intended to be used
+     for optimization.  */
+  tree minlen;
+  tree maxlen;
+  tree maxbound;
+  /* When non-null, NONSTR refers to the declaration known to store
+     an unterminated constant character array, as in:
+     const char s[] = { 'a', 'b', 'c' };
+     It is used to diagnose uses of such arrays in functions such as
+     strlen() that expect a nul-terminated string as an argument.  */
   tree decl;
-  tree len;
+  /* Non-constant offset from the beginning of a string not accounted
+     for in the length range.  Used to improve diagnostics.  */
   tree off;
 };
 
diff --git a/gcc/calls.c b/gcc/calls.c
index 8978d3b..b300d2b 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1556,9 +1556,11 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
   /* The bound argument to a bounded string function like strncpy.  */
   tree bound = NULL_TREE;
 
-  /* The range of lengths of a string argument to one of the comparison
-     functions.  If the length is less than the bound it is used instead.  */
-  tree lenrng[2] = { NULL_TREE, NULL_TREE };
+  /* The longest known or possible string argument to one of the comparison
+     functions.  If the length is less than the bound it is used instead.
+     Since the length is only used for warning and not for code generation
+     disable strict mode in the calls to get_range_strlen below.  */
+  tree maxlen = NULL_TREE;
 
   /* It's safe to call "bounded" string functions with a non-string
      argument since the functions provide an explicit bound for this
@@ -1578,11 +1580,15 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
 	   and to adjust the range of the bound of the bounded ones.  */
 	for (unsigned argno = 0;
 	     argno < MIN (nargs, 2)
-	     && !(lenrng[1] && TREE_CODE (lenrng[1]) == INTEGER_CST); argno++)
+	       && !(maxlen && TREE_CODE (maxlen) == INTEGER_CST); argno++)
 	  {
 	    tree arg = CALL_EXPR_ARG (exp, argno);
 	    if (!get_attr_nonstring_decl (arg))
-	      get_range_strlen (arg, lenrng);
+	      {
+		c_strlen_data lendata = { };
+		get_range_strlen (arg, &lendata, /* eltsize = */ 1);
+		maxlen = lendata.maxbound;
+	      }
 	  }
       }
       /* Fall through.  */
@@ -1603,8 +1609,11 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
       {
 	tree arg = CALL_EXPR_ARG (exp, 0);
 	if (!get_attr_nonstring_decl (arg))
-	  get_range_strlen (arg, lenrng);
-
+	  {
+	    c_strlen_data lendata = { };
+	    get_range_strlen (arg, &lendata, /* eltsize = */ 1);
+	    maxlen = lendata.maxbound;
+	  }
 	if (nargs > 1)
 	  bound = CALL_EXPR_ARG (exp, 1);
 	break;
@@ -1645,28 +1654,28 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
 	}
     }
 
-  if (lenrng[1] && TREE_CODE (lenrng[1]) == INTEGER_CST)
+  if (maxlen && !integer_all_onesp (maxlen))
     {
       /* Add one for the nul.  */
-      lenrng[1] = const_binop (PLUS_EXPR, TREE_TYPE (lenrng[1]),
-			       lenrng[1], size_one_node);
+      maxlen = const_binop (PLUS_EXPR, TREE_TYPE (maxlen), maxlen,
+			    size_one_node);
 
       if (!bndrng[0])
 	{
 	  /* Conservatively use the upper bound of the lengths for
 	     both the lower and the upper bound of the operation.  */
-	  bndrng[0] = lenrng[1];
-	  bndrng[1] = lenrng[1];
+	  bndrng[0] = maxlen;
+	  bndrng[1] = maxlen;
 	  bound = void_type_node;
 	}
       else
 	{
 	  /* Replace the bound on the operation with the upper bound
 	     of the length of the string if the latter is smaller.  */
-	  if (tree_int_cst_lt (lenrng[1], bndrng[0]))
-	    bndrng[0] = lenrng[1];
-	  else if (tree_int_cst_lt (lenrng[1], bndrng[1]))
-	    bndrng[1] = lenrng[1];
+	  if (tree_int_cst_lt (maxlen, bndrng[0]))
+	    bndrng[0] = maxlen;
+	  else if (tree_int_cst_lt (maxlen, bndrng[1]))
+	    bndrng[1] = maxlen;
 	}
     }
 
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 67c8cfa..7eb9697 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -66,6 +66,25 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-vector-builder.h"
 #include "tree-ssa-strlen.h"
 
+enum strlen_range_kind {
+  /* Compute the exact constant string length.  */
+  SRK_STRLEN,
+  /* Compute the maximum constant string length.  */
+  SRK_STRLENMAX,
+  /* Compute a range of string lengths bounded by object sizes.  When
+     the length of a string cannot be determined, consider as the upper
+     bound the size of the enclosing object the string may be a member
+     or element of.  Also determine the size of the largest character
+     array the string may refer to.  */
+  SRK_LENRANGE,
+  /* Determine the integer value of the argument (not string length).  */
+  SRK_INT_VALUE
+};
+
+static bool
+get_range_strlen (tree, bitmap *, strlen_range_kind, c_strlen_data *,
+		  unsigned);
+
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
    We can get declarations that are not possible to reference for various
@@ -1258,221 +1277,289 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
   return true;
 }
 
-
-/* Obtain the minimum and maximum string length or minimum and maximum
-   value of ARG in LENGTH[0] and LENGTH[1], respectively.
-   If ARG is an SSA name variable, follow its use-def chains.  When
-   TYPE == 0, if LENGTH[1] is not equal to the length we determine or
-   if we are unable to determine the length or value, return false.
-   VISITED is a bitmap of visited variables.
-   TYPE is 0 if string length should be obtained, 1 for maximum string
-   length and 2 for maximum value ARG can have.
-   When FUZZY is non-zero and the length of a string cannot be determined,
-   the function instead considers as the maximum possible length the
-   size of a character array it may refer to.  If FUZZY is 2, it will handle
-   PHIs and COND_EXPRs optimistically, if we can determine string length
-   minimum and maximum, it will use the minimum from the ones where it
-   can be determined.
-   Set *FLEXP to true if the range of the string lengths has been
-   obtained from the upper bound of an array at the end of a struct.
-   Such an array may hold a string that's longer than its upper bound
-   due to it being used as a poor-man's flexible array member.
-   Pass NONSTR through to children.
-   ELTSIZE is 1 for normal single byte character strings, and 2 or
-   4 for wide characer strings.  ELTSIZE is by default 1.  */
+/* Helper of get_range_strlen for ARG that is not SSA_NAME.  */
 
 static bool
-get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
-		  int fuzzy, bool *flexp, unsigned eltsize, tree *nonstr)
+get_range_strlen_tree (tree arg, bitmap *visited, strlen_range_kind rkind,
+		       c_strlen_data *pdata, unsigned eltsize)
 {
-  tree var, val = NULL_TREE;
-  gimple *def_stmt;
+  gcc_assert (TREE_CODE (arg) != SSA_NAME);
 
-  /* The minimum and maximum length.  */
-  tree *const minlen = length;
-  tree *const maxlen = length + 1;
+  /* The length computed by this invocation of the function.  */
+  tree val = NULL_TREE;
 
-  if (TREE_CODE (arg) != SSA_NAME)
+  /* True if VAL is an optimistic (tight) bound determined from
+     the size of the character array in which the string may be
+     stored.  In that case, the computed VAL is used to set
+     PDATA->MAXBOUND.  */
+  bool tight_bound = false;
+
+  /* We can end up with &(*iftmp_1)[0] here as well, so handle it.  */
+  if (TREE_CODE (arg) == ADDR_EXPR
+      && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
     {
-      /* We can end up with &(*iftmp_1)[0] here as well, so handle it.  */
-      if (TREE_CODE (arg) == ADDR_EXPR
-	  && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
+      tree op = TREE_OPERAND (arg, 0);
+      if (integer_zerop (TREE_OPERAND (op, 1)))
 	{
-	  tree op = TREE_OPERAND (arg, 0);
-	  if (integer_zerop (TREE_OPERAND (op, 1)))
-	    {
-	      tree aop0 = TREE_OPERAND (op, 0);
-	      if (TREE_CODE (aop0) == INDIRECT_REF
-		  && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
-		return get_range_strlen (TREE_OPERAND (aop0, 0), length,
-					 visited, type, fuzzy, flexp,
-					 eltsize, nonstr);
-	    }
-	  else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF && fuzzy)
-	    {
-	      /* Fail if an array is the last member of a struct object
-		 since it could be treated as a (fake) flexible array
-		 member.  */
-	      tree idx = TREE_OPERAND (op, 1);
-
-	      arg = TREE_OPERAND (op, 0);
-	      tree optype = TREE_TYPE (arg);
-	      if (tree dom = TYPE_DOMAIN (optype))
-		if (tree bound = TYPE_MAX_VALUE (dom))
-		  if (TREE_CODE (bound) == INTEGER_CST
-		      && TREE_CODE (idx) == INTEGER_CST
-		      && tree_int_cst_lt (bound, idx))
-		    return false;
-	    }
+	  tree aop0 = TREE_OPERAND (op, 0);
+	  if (TREE_CODE (aop0) == INDIRECT_REF
+	      && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
+	    return get_range_strlen (TREE_OPERAND (aop0, 0), visited, rkind,
+				     pdata, eltsize);
 	}
-
-      if (type == 2)
+      else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF
+	       && rkind == SRK_LENRANGE)
 	{
-	  val = arg;
-	  if (TREE_CODE (val) != INTEGER_CST
-	      || tree_int_cst_sgn (val) < 0)
-	    return false;
+	  /* Fail if an array is the last member of a struct object
+	     since it could be treated as a (fake) flexible array
+	     member.  */
+	  tree idx = TREE_OPERAND (op, 1);
+
+	  arg = TREE_OPERAND (op, 0);
+	  tree optype = TREE_TYPE (arg);
+	  if (tree dom = TYPE_DOMAIN (optype))
+	    if (tree bound = TYPE_MAX_VALUE (dom))
+	      if (TREE_CODE (bound) == INTEGER_CST
+		  && TREE_CODE (idx) == INTEGER_CST
+		  && tree_int_cst_lt (bound, idx))
+		return false;
 	}
-      else
-	{
-	  c_strlen_data data;
-	  memset (&data, 0, sizeof (c_strlen_data));
-	  val = c_strlen (arg, 1, &data, eltsize);
+    }
 
-	  /* If we potentially had a non-terminated string, then
-	     bubble that information up to the caller.  */
-	  if (!val && data.decl)
-	    {
-	      *nonstr = data.decl;
-	      *minlen = data.len;
-	      *maxlen = data.len;
-	      return type == 0 ? false : true;
-	    }
+  if (rkind == SRK_INT_VALUE)
+    {
+      /* We are computing the maximum value (not string length).  */
+      val = arg;
+      if (TREE_CODE (val) != INTEGER_CST
+	  || tree_int_cst_sgn (val) < 0)
+	return false;
+    }
+  else
+    {
+      c_strlen_data lendata = { };
+      val = c_strlen (arg, 1, &lendata, eltsize);
+      if (!val && lendata.decl)
+	{
+	  /* ARG refers to an unterminated const character array
+	     DATA>DECL with size DATA.LEN.  */
+	  val = lendata.minlen;
+	  pdata->decl = lendata.decl;
 	}
+    }
 
-      if (!val && fuzzy)
+  if (!val && rkind == SRK_LENRANGE)
+    {
+      if (TREE_CODE (arg) == ADDR_EXPR)
+	return get_range_strlen (TREE_OPERAND (arg, 0), visited, rkind,
+				 pdata, eltsize);
+
+      if (TREE_CODE (arg) == ARRAY_REF)
 	{
-	  if (TREE_CODE (arg) == ADDR_EXPR)
-	    return get_range_strlen (TREE_OPERAND (arg, 0), length,
-				     visited, type, fuzzy, flexp,
-				     eltsize, nonstr);
+	  tree optype = TREE_TYPE (TREE_OPERAND (arg, 0));
 
-	  if (TREE_CODE (arg) == ARRAY_REF)
-	    {
-	      tree type = TREE_TYPE (TREE_OPERAND (arg, 0));
+	  /* Determine the "innermost" array type.  */
+	  while (TREE_CODE (optype) == ARRAY_TYPE
+		 && TREE_CODE (TREE_TYPE (optype)) == ARRAY_TYPE)
+	    optype = TREE_TYPE (optype);
 
-	      /* Determine the "innermost" array type.  */
-	      while (TREE_CODE (type) == ARRAY_TYPE
-		     && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
-		type = TREE_TYPE (type);
+	  /* Avoid arrays of pointers.  */
+	  tree eltype = TREE_TYPE (optype);
+	  if (TREE_CODE (optype) != ARRAY_TYPE
+	      || !INTEGRAL_TYPE_P (eltype))
+	    return false;
 
-	      /* Avoid arrays of pointers.  */
-	      tree eltype = TREE_TYPE (type);
-	      if (TREE_CODE (type) != ARRAY_TYPE
-		  || !INTEGRAL_TYPE_P (eltype))
-		return false;
+	  /* Fail when the array bound is unknown or zero.  */
+	  val = TYPE_SIZE_UNIT (optype);
+	  if (!val || integer_zerop (val))
+	    return false;
 
-	      val = TYPE_SIZE_UNIT (type);
-	      if (!val || integer_zerop (val))
-		return false;
+	  val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val, integer_one_node);
+	  /* Set the minimum size to zero since the string in
+	     the array could have zero length.  */
+	  pdata->minlen = ssize_int (0);
+	  tight_bound = true;
+	}
+      else if (TREE_CODE (arg) == COMPONENT_REF
+	       && (TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1)))
+		   == ARRAY_TYPE))
+	{
+	  /* Use the type of the member array to determine the upper
+	     bound on the length of the array.  This may be overly
+	     optimistic if the array itself isn't NUL-terminated and
+	     the caller relies on the subsequent member to contain
+	     the NUL but that would only be considered valid if
+	     the array was the last member of a struct.  */
 
-	      val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
-				 integer_one_node);
-	      /* Set the minimum size to zero since the string in
-		 the array could have zero length.  */
-	      *minlen = ssize_int (0);
+	  tree fld = TREE_OPERAND (arg, 1);
 
-	      if (TREE_CODE (TREE_OPERAND (arg, 0)) == COMPONENT_REF
-		  && type == TREE_TYPE (TREE_OPERAND (arg, 0))
-		  && array_at_struct_end_p (TREE_OPERAND (arg, 0)))
-		*flexp = true;
-	    }
-	  else if (TREE_CODE (arg) == COMPONENT_REF
-		   && (TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1)))
-		       == ARRAY_TYPE))
+	  tree optype = TREE_TYPE (fld);
+
+	  /* Determine the "innermost" array type.  */
+	  while (TREE_CODE (optype) == ARRAY_TYPE
+		 && TREE_CODE (TREE_TYPE (optype)) == ARRAY_TYPE)
+	    optype = TREE_TYPE (optype);
+
+	  /* Fail when the array bound is unknown or zero.  */
+	  val = TYPE_SIZE_UNIT (optype);
+	  if (!val || integer_zerop (val))
+	    return false;
+
+	  val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val, integer_one_node);
+	  /* Set the minimum size to zero since the string in
+	     the array could have zero length.  */
+	  pdata->minlen = ssize_int (0);
+
+	  /* The array size determined above is an optimistic bound
+	     on the length.  If the array isn't nul-terminated the
+	     length computed by the library function would be greater.
+	     Even though using strlen to cross the subobject boundary
+	     is undefined, avoid drawing conclusions from the member
+	     type about the length here.  */
+	  tight_bound = true;
+	}
+      else if (VAR_P (arg))
+	{
+	  /* Avoid handling pointers to arrays.  GCC might misuse
+	     a pointer to an array of one bound to point to an array
+	     object of a greater bound.  */
+	  tree argtype = TREE_TYPE (arg);
+	  if (TREE_CODE (argtype) == ARRAY_TYPE)
 	    {
-	      /* Use the type of the member array to determine the upper
-		 bound on the length of the array.  This may be overly
-		 optimistic if the array itself isn't NUL-terminated and
-		 the caller relies on the subsequent member to contain
-		 the NUL but that would only be considered valid if
-		 the array were the last member of a struct.
-		 Set *FLEXP to true if the array whose bound is being
-		 used is at the end of a struct.  */
-	      if (array_at_struct_end_p (arg))
-		*flexp = true;
-
-	      arg = TREE_OPERAND (arg, 1);
-
-	      tree type = TREE_TYPE (arg);
-
-	      while (TREE_CODE (type) == ARRAY_TYPE
-		     && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
-		type = TREE_TYPE (type);
-
-	      /* Fail when the array bound is unknown or zero.  */
-	      val = TYPE_SIZE_UNIT (type);
-	      if (!val || integer_zerop (val))
+	      val = TYPE_SIZE_UNIT (argtype);
+	      if (!val
+		  || TREE_CODE (val) != INTEGER_CST
+		  || integer_zerop (val))
 		return false;
-	      val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
-				 integer_one_node);
+	      val = wide_int_to_tree (TREE_TYPE (val),
+				      wi::sub (wi::to_wide (val), 1));
 	      /* Set the minimum size to zero since the string in
 		 the array could have zero length.  */
-	      *minlen = ssize_int (0);
-	    }
-
-	  if (VAR_P (arg))
-	    {
-	      tree type = TREE_TYPE (arg);
-	      if (POINTER_TYPE_P (type))
-		type = TREE_TYPE (type);
-
-	      if (TREE_CODE (type) == ARRAY_TYPE)
-		{
-		  val = TYPE_SIZE_UNIT (type);
-		  if (!val
-		      || TREE_CODE (val) != INTEGER_CST
-		      || integer_zerop (val))
-		    return false;
-		  val = wide_int_to_tree (TREE_TYPE (val),
-					  wi::sub (wi::to_wide (val), 1));
-		  /* Set the minimum size to zero since the string in
-		     the array could have zero length.  */
-		  *minlen = ssize_int (0);
-		}
+	      pdata->minlen = ssize_int (0);
 	    }
 	}
+    }
 
-      if (!val)
-	return false;
+  if (!val)
+    return false;
 
-      if (!*minlen
-	  || (type > 0
-	      && TREE_CODE (*minlen) == INTEGER_CST
-	      && TREE_CODE (val) == INTEGER_CST
-	      && tree_int_cst_lt (val, *minlen)))
-	*minlen = val;
+  /* Adjust the lower bound on the string length as necessary.  */
+  if (!pdata->minlen
+      || (rkind != SRK_STRLEN
+	  && TREE_CODE (pdata->minlen) == INTEGER_CST
+	  && TREE_CODE (val) == INTEGER_CST
+	  && tree_int_cst_lt (val, pdata->minlen)))
+    pdata->minlen = val;
 
-      if (*maxlen)
+  if (pdata->maxbound)
+    {
+      /* Adjust the tighter (more optimistic) string length bound
+	 if necessary and proceed to adjust the more conservative
+	 bound.  */
+      if (TREE_CODE (val) == INTEGER_CST)
 	{
-	  if (type > 0)
+	  if (TREE_CODE (pdata->maxbound) == INTEGER_CST)
 	    {
-	      if (TREE_CODE (*maxlen) != INTEGER_CST
-		  || TREE_CODE (val) != INTEGER_CST)
-		return false;
+	      if (tree_int_cst_lt (pdata->maxbound, val))
+		pdata->maxbound = val;
+	    }
+	  else
+	    pdata->maxbound = build_all_ones_cst (size_type_node);
+	}
+      else
+	pdata->maxbound = val;
+    }
+  else
+    pdata->maxbound = val;
 
-	      if (tree_int_cst_lt (*maxlen, val))
-		*maxlen = val;
-	      return true;
+  if (tight_bound)
+    {
+      /* VAL computed above represents an otpimistically tight bound
+	 on the length of the string based on the referenced object's
+	 or subobject's type.  Determine the conservative upper bound
+	 based on the enclosing object's size if possible.  */
+      if (rkind == SRK_LENRANGE)
+	{
+	  poly_int64 offset;
+	  tree base = get_addr_base_and_unit_offset (arg, &offset);
+	  if (!base)
+	    {
+	      /* When the call above fails due to a non-constant offset
+		 assume the offset is zero and use the size of the whole
+		 enclosing object instead.  */
+	      base = get_base_address (arg);
+	      offset = 0;
+	    }
+	  /* If the base object is a pointer no upper bound on the length
+	     can be determined.  Otherwise the maximum length is equal to
+	     the size of the enclosing object minus the offset of
+	     the referenced subobject minus 1 (for the terminating nul).  */
+	  tree type = TREE_TYPE (base);
+	  if (TREE_CODE (type) == POINTER_TYPE
+	      || !VAR_P (base) || !(val = DECL_SIZE_UNIT (base)))
+	    val = build_all_ones_cst (size_type_node);
+	  else
+	    {
+	      val = DECL_SIZE_UNIT (base);
+	      val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
+				 size_int (offset + 1));
 	    }
-	  else if (simple_cst_equal (val, *maxlen) != 1)
-	    return false;
 	}
+      else
+	return false;
+    }
 
-      *maxlen = val;
-      return true;
+  if (pdata->maxlen)
+    {
+      /* Adjust the more conservative bound if possible/necessary
+	 and fail otherwise.  */
+      if (rkind != SRK_STRLEN)
+	{
+	  if (TREE_CODE (pdata->maxlen) != INTEGER_CST
+	      || TREE_CODE (val) != INTEGER_CST)
+	    return false;
+
+	  if (tree_int_cst_lt (pdata->maxlen, val))
+	    pdata->maxlen = val;
+	  return true;
+	}
+      else if (simple_cst_equal (val, pdata->maxlen) != 1)
+	{
+	  /* Fail if the length of this ARG is different from that
+	     previosuly determined from another ARG.  */
+	  return false;
+	}
     }
 
+  pdata->maxlen = val;
+
+  /* When not computing a size-based range, fail if the conservative
+     bound could  not be determined.  */
+  return rkind == SRK_LENRANGE || !integer_all_onesp (val);
+}
+
+/* For an ARG referencing one or more strings, try to obtain the range
+   of their lengths, or the size of the largest array ARG referes to if
+   the range of lengths cannot be determined, and store all in *PDATA.
+   For an integer ARG (when RKIND == SRK_INT_VALUE), try to determine
+   the maximum constant value.
+   If ARG is an SSA_NAME, follow its use-def chains.  When RKIND ==
+   SRK_STRLEN, then if PDATA->MAXLEN is not equal to the determined
+   length or if we are unable to determine the length, return false.
+   VISITED is a bitmap of visited variables.
+   RKIND determines the kind of value or range to obtain (see
+   strlen_range_kind).
+   Set PDATA->DECL if ARG refers to an unterminated constant array.
+   On input, set ELTSIZE to 1 for normal single byte character strings,
+   and either 2 or 4 for wide characer strings (the size of wchar_t).
+   Return true if *PDATA was successfully populated and false otherwise.  */
+
+static bool
+get_range_strlen (tree arg, bitmap *visited, strlen_range_kind rkind,
+		  c_strlen_data *pdata, unsigned eltsize)
+{
+  if (TREE_CODE (arg) != SSA_NAME)
+    return get_range_strlen_tree (arg, visited, rkind, pdata, eltsize);
+
   /* If ARG is registered for SSA update we cannot look at its defining
      statement.  */
   if (name_registered_for_update_p (arg))
@@ -1484,21 +1571,17 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
   if (!bitmap_set_bit (*visited, SSA_NAME_VERSION (arg)))
     return true;
 
-  var = arg;
-  def_stmt = SSA_NAME_DEF_STMT (var);
+  tree var = arg;
+  gimple *def_stmt = SSA_NAME_DEF_STMT (var);
 
   switch (gimple_code (def_stmt))
     {
       case GIMPLE_ASSIGN:
-        /* The RHS of the statement defining VAR must either have a
-           constant length or come from another SSA_NAME with a constant
-           length.  */
         if (gimple_assign_single_p (def_stmt)
             || gimple_assign_unary_nop_p (def_stmt))
           {
             tree rhs = gimple_assign_rhs1 (def_stmt);
-	    return get_range_strlen (rhs, length, visited, type, fuzzy, flexp,
-				     eltsize, nonstr);
+	    return get_range_strlen (rhs, visited, rkind, pdata, eltsize);
           }
 	else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR)
 	  {
@@ -1506,21 +1589,27 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
 			    gimple_assign_rhs3 (def_stmt) };
 
 	    for (unsigned int i = 0; i < 2; i++)
-	      if (!get_range_strlen (ops[i], length, visited, type, fuzzy,
-				     flexp, eltsize, nonstr))
+	      if (!get_range_strlen (ops[i], visited, rkind,  pdata, eltsize))
 		{
-		  if (fuzzy == 2)
-		    *maxlen = build_all_ones_cst (size_type_node);
-		  else
+		  if (rkind != SRK_LENRANGE)
 		    return false;
+		  /* Set the upper bound to the maximum to prevent
+		     it from being adjusted in the next iteration but
+		     leave MINLEN and the more conservative MAXBOUND
+		     determined so far alone (or leave them null if
+		     they haven't been set yet).  That the MINLEN is
+		     in fact zero can be determined from MAXLEN being
+		     unbounded but the discovered minimum is used for
+		     diagnostics.  */
+		  pdata->maxlen = build_all_ones_cst (size_type_node);
 		}
 	    return true;
 	  }
         return false;
 
       case GIMPLE_PHI:
-	/* All the arguments of the PHI node must have the same constant
-	   length.  */
+	/* Unless RKIND == SRK_LENRANGE, all arguments of the PHI node
+	   must have a constant length.  */
 	for (unsigned i = 0; i < gimple_phi_num_args (def_stmt); i++)
           {
             tree arg = gimple_phi_arg (def_stmt, i)->def;
@@ -1534,13 +1623,19 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
             if (arg == gimple_phi_result (def_stmt))
               continue;
 
-	    if (!get_range_strlen (arg, length, visited, type, fuzzy, flexp,
-				   eltsize, nonstr))
+	    if (!get_range_strlen (arg, visited, rkind, pdata, eltsize))
 	      {
-		if (fuzzy == 2)
-		  *maxlen = build_all_ones_cst (size_type_node);
-		else
+		if (rkind != SRK_LENRANGE)
 		  return false;
+		  /* Set the upper bound to the maximum to prevent
+		     it from being adjusted in the next iteration but
+		     leave MINLEN and the more conservative MAXBOUND
+		     determined so far alone (or leave them null if
+		     they haven't been set yet).  That the MINLEN is
+		     in fact zero can be determined from MAXLEN being
+		     unbounded but the discovered minimum is used for
+		     diagnostics.  */
+		pdata->maxlen = build_all_ones_cst (size_type_node);
 	      }
           }
         return true;
@@ -1550,78 +1645,72 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
     }
 }
 
-/* Determine the minimum and maximum value or string length that ARG
-   refers to and store each in the first two elements of MINMAXLEN.
-   For expressions that point to strings of unknown lengths that are
-   character arrays, use the upper bound of the array as the maximum
-   length.  For example, given an expression like 'x ? array : "xyz"'
-   and array declared as 'char array[8]', MINMAXLEN[0] will be set
-   to 0 and MINMAXLEN[1] to 7, the longest string that could be
-   stored in array.
-   Return true if the range of the string lengths has been obtained
-   from the upper bound of an array at the end of a struct.  Such
-   an array may hold a string that's longer than its upper bound
-   due to it being used as a poor-man's flexible array member.
-
-   STRICT is true if it will handle PHIs and COND_EXPRs conservatively
-   and false if PHIs and COND_EXPRs are to be handled optimistically,
-   if we can determine string length minimum and maximum; it will use
-   the minimum from the ones where it can be determined.
-   STRICT false should be only used for warning code.
-   When non-null, clear *NONSTR if ARG refers to a constant array
-   that is known not be nul-terminated.  Otherwise set it to
-   the declaration of the constant non-terminated array.
-
-   ELTSIZE is 1 for normal single byte character strings, and 2 or
-   4 for wide characer strings.  ELTSIZE is by default 1.  */
+/* Try to obtain the range of the lengths of the string(s) referenced
+   by ARG, or the size of the largest array ARG refers to if the range
+   of lengths cannot be determined, and store all in *PDATA.
+   Return true if the range [PDATA->MINLEN, PDATA->MAXLEN] is suitable
+   for optimization.  Returning false means that a nonzero PDATA->MINLEN
+   doesn't reflect the true lower bound of the range  when PDATA->MAXLEN
+   is -1 (in that case, the actual range is indeterminate, i.e.,
+   [0, PTRDIFF_MAX - 2].  */
 
 bool
-get_range_strlen (tree arg, tree minmaxlen[2], unsigned eltsize,
-		  bool strict, tree *nonstr /* = NULL */)
+get_range_strlen (tree arg, c_strlen_data *pdata, unsigned eltsize)
 {
   bitmap visited = NULL;
 
-  minmaxlen[0] = NULL_TREE;
-  minmaxlen[1] = NULL_TREE;
-
-  tree nonstrbuf;
-  if (!nonstr)
-    nonstr = &nonstrbuf;
-  *nonstr = NULL_TREE;
-
-  bool flexarray = false;
-  if (!get_range_strlen (arg, minmaxlen, &visited, 1, strict ? 1 : 2,
-			 &flexarray, eltsize, nonstr))
+  if (!get_range_strlen (arg, &visited, SRK_LENRANGE, pdata, eltsize))
     {
-      minmaxlen[0] = NULL_TREE;
-      minmaxlen[1] = NULL_TREE;
+      /* 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);
+
+  /* Unless it's null, leave the more conservative MAXBOUND unchanged.  */
+  if (!pdata->maxbound)
+    pdata->maxbound = pdata->maxlen;
 
   if (visited)
     BITMAP_FREE (visited);
 
-  return flexarray;
+  /* Return true if the range [PDATA->MINLEN, PDATA->MAXLEN] is
+     the true length range.  */
+  return !integer_all_onesp (pdata->maxlen);
 }
 
-/* Return the maximum string length for ARG, counting by TYPE
-   (1, 2 or 4 for normal or wide chars).  NONSTR indicates
-   if the caller is prepared to handle unterminated strings.
+/* Return the maximum value for ARG given RKIND (see strlen_range_kind).
+   For ARG of pointer types, NONSTR indicates if the caller is prepared
+   to handle unterminated strings.   For integer ARG and when RKIND ==
+   SRK_INT_VALUE, NONSTR must be null.
 
-   If an unterminated string is discovered and our caller handles
-   unterminated strings, then bubble up the offending DECL and
+   If an unterminated array is discovered and our caller handles
+   unterminated arrays, then bubble up the offending DECL and
    return the maximum size.  Otherwise return NULL.  */
 
-tree
-get_maxval_strlen (tree arg, int type, tree *nonstr /* = NULL */)
+static tree
+get_maxval_strlen (tree arg, strlen_range_kind rkind, tree *nonstr = NULL)
 {
+  /* A non-null NONSTR is meaningless when determining the maximum
+     value of an integer ARG.  */
+  gcc_assert (rkind != SRK_INT_VALUE || nonstr == NULL);
+  /* ARG must have an integral type when RKIND says so.  */
+  gcc_assert (rkind != SRK_INT_VALUE || INTEGRAL_TYPE_P (TREE_TYPE (arg)));
+
   bitmap visited = NULL;
-  tree len[2] = { NULL_TREE, NULL_TREE };
 
-  bool dummy;
-  /* Set to non-null if ARG refers to an untermianted array.  */
-  tree mynonstr = NULL_TREE;
-  if (!get_range_strlen (arg, len, &visited, type, 0, &dummy, 1, &mynonstr))
-    len[1] = NULL_TREE;
+  c_strlen_data lendata = { };
+
+  /* Redet DATA.MAXLEN if the call fails or when DATA.MAXLEN
+     is unbounded.  */
+  if (!get_range_strlen (arg, &visited, rkind, &lendata, /* eltsize = */1))
+    lendata.maxlen = NULL_TREE;
+  else if (lendata.maxlen && integer_all_onesp (lendata.maxlen))
+    lendata.maxlen = NULL_TREE;
+
   if (visited)
     BITMAP_FREE (visited);
 
@@ -1630,12 +1719,12 @@ get_maxval_strlen (tree arg, int type, tree *nonstr /* = NULL */)
       /* For callers prepared to handle unterminated arrays set
 	 *NONSTR to point to the declaration of the array and return
 	 the maximum length/size. */
-      *nonstr = mynonstr;
-      return len[1];
+      *nonstr = lendata.decl;
+      return lendata.maxlen;
     }
 
   /* Fail if the constant array isn't nul-terminated.  */
-  return mynonstr ? NULL_TREE : len[1];
+  return lendata.decl ? NULL_TREE : lendata.maxlen;
 }
 
 
@@ -1680,7 +1769,7 @@ gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
 
   /* Set to non-null if ARG refers to an unterminated array.  */
   tree nonstr = NULL;
-  tree len = get_maxval_strlen (src, 0, &nonstr);
+  tree len = get_maxval_strlen (src, SRK_STRLEN, &nonstr);
 
   if (nonstr)
     {
@@ -1726,7 +1815,7 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
 
 	  /* Warn about the lack of nul termination: the result is not
 	     a (nul-terminated) string.  */
-	  tree slen = get_maxval_strlen (src, 0);
+	  tree slen = get_maxval_strlen (src, SRK_STRLEN);
 	  if (slen && !integer_zerop (slen))
 	    warning_at (loc, OPT_Wstringop_truncation,
 			"%G%qD destination unchanged after copying no bytes "
@@ -1748,7 +1837,7 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
     return false;
 
   /* Now, we must be passed a constant src ptr parameter.  */
-  tree slen = get_maxval_strlen (src, 0);
+  tree slen = get_maxval_strlen (src, SRK_STRLEN);
   if (!slen || TREE_CODE (slen) != INTEGER_CST)
     return false;
 
@@ -1970,7 +2059,7 @@ gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi, tree dst, tree src)
 
   /* If the length of the source string isn't computable don't
      split strcat into strlen and memcpy.  */
-  tree len = get_maxval_strlen (src, 0);
+  tree len = get_maxval_strlen (src, SRK_STRLEN);
   if (! len)
     return false;
 
@@ -2486,7 +2575,7 @@ gimple_fold_builtin_fputs (gimple_stmt_iterator *gsi,
 
   /* Get the length of the string passed to fputs.  If the length
      can't be determined, punt.  */
-  tree len = get_maxval_strlen (arg0, 0);
+  tree len = get_maxval_strlen (arg0, SRK_STRLEN);
   if (!len
       || TREE_CODE (len) != INTEGER_CST)
     return false;
@@ -2574,7 +2663,7 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
   if (! tree_fits_uhwi_p (size))
     return false;
 
-  tree maxlen = get_maxval_strlen (len, 2);
+  tree maxlen = get_maxval_strlen (len, SRK_INT_VALUE);
   if (! integer_all_onesp (size))
     {
       if (! tree_fits_uhwi_p (len))
@@ -2675,7 +2764,7 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
   if (! tree_fits_uhwi_p (size))
     return false;
 
-  tree maxlen = get_maxval_strlen (src, 1);
+  tree maxlen = get_maxval_strlen (src, SRK_STRLENMAX);
   if (! integer_all_onesp (size))
     {
       len = c_strlen (src, 1);
@@ -2771,7 +2860,7 @@ gimple_fold_builtin_stxncpy_chk (gimple_stmt_iterator *gsi,
   if (! tree_fits_uhwi_p (size))
     return false;
 
-  tree maxlen = get_maxval_strlen (len, 2);
+  tree maxlen = get_maxval_strlen (len, SRK_INT_VALUE);
   if (! integer_all_onesp (size))
     {
       if (! tree_fits_uhwi_p (len))
@@ -2909,7 +2998,7 @@ gimple_fold_builtin_snprintf_chk (gimple_stmt_iterator *gsi,
 
   if (! integer_all_onesp (size))
     {
-      tree maxlen = get_maxval_strlen (len, 2);
+      tree maxlen = get_maxval_strlen (len, SRK_INT_VALUE);
       if (! tree_fits_uhwi_p (len))
 	{
 	  /* If LEN is not constant, try MAXLEN too.
@@ -3151,7 +3240,7 @@ gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
       tree orig_len = NULL_TREE;
       if (gimple_call_lhs (stmt))
 	{
-	  orig_len = get_maxval_strlen (orig, 0);
+	  orig_len = get_maxval_strlen (orig, SRK_STRLEN);
 	  if (!orig_len)
 	    return false;
 	}
@@ -3284,7 +3373,7 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
       if (!orig)
 	return false;
 
-      tree orig_len = get_maxval_strlen (orig, 0);
+      tree orig_len = get_maxval_strlen (orig, SRK_STRLEN);
       if (!orig_len || TREE_CODE (orig_len) != INTEGER_CST)
 	return false;
 
@@ -3595,21 +3684,19 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
   wide_int minlen;
   wide_int maxlen;
 
-  /* Set to non-null if ARG refers to an unterminated array.  */
-  tree nonstr;
-  tree lenrange[2];
-  if (!get_range_strlen (arg, lenrange, 1, true, &nonstr)
-      && !nonstr
-      && lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST
-      && lenrange[1] && TREE_CODE (lenrange[1]) == INTEGER_CST)
+  c_strlen_data lendata = { };
+  if (get_range_strlen (arg, &lendata, /* eltsize = */ 1)
+      && !lendata.decl
+      && lendata.minlen && TREE_CODE (lendata.minlen) == INTEGER_CST
+      && lendata.maxlen && TREE_CODE (lendata.maxlen) == INTEGER_CST)
     {
       /* The range of lengths refers to either a single constant
 	 string or to the longest and shortest constant string
 	 referenced by the argument of the strlen() call, or to
 	 the strings that can possibly be stored in the arrays
 	 the argument refers to.  */
-      minlen = wi::to_wide (lenrange[0]);
-      maxlen = wi::to_wide (lenrange[1]);
+      minlen = wi::to_wide (lendata.minlen);
+      maxlen = wi::to_wide (lendata.maxlen);
     }
   else
     {
@@ -3621,16 +3708,18 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
 
   if (minlen == maxlen)
     {
-      lenrange[0] = force_gimple_operand_gsi (gsi, lenrange[0], true, NULL,
-					      true, GSI_SAME_STMT);
-      replace_call_with_value (gsi, lenrange[0]);
+      /* Fold the strlen call to a constant.  */
+      tree type = TREE_TYPE (lendata.minlen);
+      tree len = force_gimple_operand_gsi (gsi,
+					   wide_int_to_tree (type, minlen),
+					   true, NULL, true, GSI_SAME_STMT);
+      replace_call_with_value (gsi, len);
       return true;
     }
 
+  /* Set the strlen() range to [0, MAXLEN].  */
   if (tree lhs = gimple_call_lhs (stmt))
-    if (TREE_CODE (lhs) == SSA_NAME
-	&& INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
-      set_range_info (lhs, VR_RANGE, minlen, maxlen);
+    set_strlen_range (lhs, maxlen);
 
   return false;
 }
diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h
index 26e2727..1661bf5 100644
--- a/gcc/gimple-fold.h
+++ b/gcc/gimple-fold.h
@@ -25,9 +25,12 @@ along with GCC; see the file COPYING3.  If not see
 extern tree create_tmp_reg_or_ssa_name (tree, gimple *stmt = NULL);
 extern tree canonicalize_constructor_val (tree, tree);
 extern tree get_symbol_constant_value (tree);
-extern bool get_range_strlen (tree, tree[2], unsigned = 1,
-			      bool = false, tree * = NULL);
-extern tree get_maxval_strlen (tree, int, tree * = NULL);
+
+/* Defined in builtins.h.  */
+struct c_strlen_data;
+extern bool get_range_strlen (tree, c_strlen_data *, unsigned = 1)
+  ATTRIBUTE_NONNULL (1) ATTRIBUTE_NONNULL (2);
+
 extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
 extern bool fold_stmt (gimple_stmt_iterator *);
 extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree));
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index 456a7d4..198b103 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -2001,91 +2001,73 @@ get_string_length (tree str, unsigned eltsize)
   if (!str)
     return fmtresult ();
 
-  c_strlen_data data;
-  memset (&data, 0, sizeof (c_strlen_data));
-  tree slen = c_strlen (str, 1, &data, eltsize);
-  if (slen && TREE_CODE (slen) == INTEGER_CST)
-    {
-      /* The string is properly terminated and
-	 we know its length.  */
-      fmtresult res (tree_to_shwi (slen));
-      res.nonstr = NULL_TREE;
-      return res;
-    }
-  else if (!slen
-	   && data.decl
-	   && data.len
-	   && TREE_CODE (data.len) == INTEGER_CST)
-    {
-      /* STR was not properly NUL terminated, but we have
-	 length information about the unterminated string.  */
-      fmtresult res (tree_to_shwi (data.len));
-      res.nonstr = data.decl;
-      return res;
-    }
-
   /* 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 LENRANGE[1] set
-     to SIZE_MAX.  NONSTR is set to the declaration of the constant
-     array that is known not to be nul-terminated.  */
-  tree lenrange[2];
-  tree nonstr;
-  bool flexarray = get_range_strlen (str, lenrange, eltsize, false, &nonstr);
+     aren't known to point any such arrays result in LENDATA.MAXLEN
+     set to SIZE_MAX.  */
+  c_strlen_data lendata = { };
+  get_range_strlen (str, &lendata, eltsize);
+
+  /* Return the default result when nothing is known about the string. */
+  if (integer_all_onesp (lendata.maxbound)
+      && integer_all_onesp (lendata.maxlen))
+    return fmtresult ();
 
-  if (lenrange [0] || lenrange [1])
+  HOST_WIDE_INT min
+    = (tree_fits_uhwi_p (lendata.minlen)
+       ? tree_to_uhwi (lendata.minlen)
+       : 0);
+
+  HOST_WIDE_INT max
+    = (tree_fits_uhwi_p (lendata.maxbound)
+       ? tree_to_uhwi (lendata.maxbound)
+       : HOST_WIDE_INT_M1U);
+
+  const bool unbounded = integer_all_onesp (lendata.maxlen);
+
+  /* Set the max/likely counters to unbounded when a minimum is known
+     but the maximum length isn't bounded.  This implies that STR is
+     a conditional expression involving a string of known length and
+     and an expression of unknown/unbounded length.  */
+  if (min
+      && (unsigned HOST_WIDE_INT)min < HOST_WIDE_INT_M1U
+      && unbounded)
+    max = HOST_WIDE_INT_M1U;
+
+  /* get_range_strlen() returns the target value of SIZE_MAX for
+     strings of unknown length.  Bump it up to HOST_WIDE_INT_M1U
+     which may be bigger.  */
+  if ((unsigned HOST_WIDE_INT)min == target_size_max ())
+    min = HOST_WIDE_INT_M1U;
+  if ((unsigned HOST_WIDE_INT)max == target_size_max ())
+    max = HOST_WIDE_INT_M1U;
+
+  fmtresult res (min, max);
+  res.nonstr = lendata.decl;
+
+  /* Set RES.KNOWNRANGE to true if and only if all strings referenced
+     by STR are known to be bounded (though not necessarily by their
+     actual length but perhaps by their maximum possible length).  */
+  if (res.range.max < target_int_max ())
     {
-      HOST_WIDE_INT min
-	= (tree_fits_uhwi_p (lenrange[0])
-	   ? tree_to_uhwi (lenrange[0])
-	   : 0);
-
-      HOST_WIDE_INT max
-	= (tree_fits_uhwi_p (lenrange[1])
-	   ? tree_to_uhwi (lenrange[1])
-	   : HOST_WIDE_INT_M1U);
-
-      /* get_range_strlen() returns the target value of SIZE_MAX for
-	 strings of unknown length.  Bump it up to HOST_WIDE_INT_M1U
-	 which may be bigger.  */
-      if ((unsigned HOST_WIDE_INT)min == target_size_max ())
-	min = HOST_WIDE_INT_M1U;
-      if ((unsigned HOST_WIDE_INT)max == target_size_max ())
-	max = HOST_WIDE_INT_M1U;
-
-      fmtresult res (min, max);
-      res.nonstr = nonstr;
-
-      /* Set RES.KNOWNRANGE to true if and only if all strings referenced
-	 by STR are known to be bounded (though not necessarily by their
-	 actual length but perhaps by their maximum possible length).  */
-      if (res.range.max < target_int_max ())
-	{
-	  res.knownrange = true;
-	  /* When the the length of the longest string is known and not
-	     excessive use it as the likely length of the string(s).  */
-	  res.range.likely = res.range.max;
-	}
-      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.  */
-	  res.range.likely = res.range.min ? res.range.min : warn_level > 1;
-	  res.range.min = 0;
-	}
-
-      /* If the range of string length has been estimated from the size
-	 of an array at the end of a struct assume that it's longer than
-	 the array bound says it is in case it's used as a poor man's
-	 flexible array member, such as in struct S { char a[4]; };  */
-      res.range.unlikely = flexarray ? HOST_WIDE_INT_MAX : res.range.max;
-
-      return res;
+      res.knownrange = true;
+      /* When the the length of the longest string is known and not
+	 excessive use it as the likely length of the string(s).  */
+      res.range.likely = res.range.max;
+    }
+  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.  */
+      res.range.likely = res.range.min ? res.range.min : warn_level > 1;
+      res.range.min = 0;
     }
 
-  return fmtresult ();
+  res.range.unlikely = unbounded ? HOST_WIDE_INT_MAX : res.range.max;
+
+  return res;
 }
 
 /* Return the minimum and maximum number of characters formatted
@@ -2325,6 +2307,8 @@ format_string (const directive &dir, tree arg, vr_values *)
 	  if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max)
 	    res.range.max = dir.prec[1];
 	  res.range.likely = dir.prec[1] ? warn_level > 1 : 0;
+	  if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.unlikely)
+	    res.range.unlikely = dir.prec[1];
 	}
       else if (slen.range.min >= target_int_max ())
 	{
@@ -2334,6 +2318,7 @@ format_string (const directive &dir, tree arg, vr_values *)
 	     empty, while at level 1 they are assumed to be one byte
 	     long.  */
 	  res.range.likely = warn_level > 1;
+	  res.range.unlikely = HOST_WIDE_INT_MAX;
 	}
       else
 	{
@@ -2343,8 +2328,6 @@ format_string (const directive &dir, tree arg, vr_values *)
 	  if (res.range.likely >= target_int_max ())
 	    res.range.likely = warn_level > 1;
 	}
-
-      res.range.unlikely = res.range.max;
     }
 
   /* If the argument isn't a nul-terminated string and the number
diff --git a/gcc/testsuite/g++.dg/init/strlen.C b/gcc/testsuite/g++.dg/init/strlen.C
new file mode 100644
index 0000000..aa8950e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/strlen.C
@@ -0,0 +1,43 @@
+// Test to verify that the strlen() optimization doesn't make assumptions
+// about the static type of the object pointed to by its argument.  See
+// the following thread for background:
+//   https://gcc.gnu.org/ml/gcc-patches/2018-08/msg00260.html
+
+// { dg-do run }
+// { dg-options "-O2 -Wall -fdump-tree-optimized" }
+
+typedef __SIZE_TYPE__ size_t;
+
+void *operator new[] (size_t, void *p) { return p; }
+
+struct S { int x; char a[1]; char b[64]; };
+
+__attribute__ ((noipa)) void
+init (char *s)
+{
+  *s++ = '1';
+  *s++ = '\0';
+}
+
+__attribute__ ((noipa)) void
+test_dynamic_type (S *p)
+{
+  // The placement new call below isn't strictly valid because it
+  // creates an object that is larger than the space of the p->a
+  // subobject in which it is created.  However, the corresponding
+  // GIMPLE considers it valid and there's apparently no way to
+  // distinguish invalid cases from ones like it that might be valid.
+  // If/when GIMPLE changes to make this possible this test can be
+  // removed.
+  char *q = new (p->a) char [16];
+
+  init (q);
+
+  if (0 == __builtin_strlen (q))
+    __builtin_abort();
+}
+
+int main ()
+{
+  test_dynamic_type (new S);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-5.c b/gcc/testsuite/gcc.c-torture/execute/strlen-5.c
new file mode 100644
index 0000000..9af57d5
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/strlen-5.c
@@ -0,0 +1,653 @@
+/* Test to verify that even strictly undefined strlen() calls with
+   unterminated character arrays yield the "expected" results when
+   the terminating nul is present in a subsequent suobobject.  */
+
+extern __SIZE_TYPE__ strlen (const char *);
+
+unsigned nfails;
+
+#define A(expr, N)						\
+  do {								\
+    const char *s = (expr);					\
+    unsigned n = strlen (s);					\
+    ((n == N)							\
+     ? 0							\
+     : (__builtin_printf ("line %i: strlen (%s = \"%s\")"	\
+			  " == %u failed\n",			\
+			  __LINE__, #expr, s, N),		\
+	++nfails));						\
+  } while (0)
+
+
+int idx;
+
+
+const char ca[][4] = {
+  { '1', '2', '3', '4' }, { '5' },
+  { '1', '2', '3', '4' }, { '5', '6' },
+  { '1', '2', '3', '4' }, { '5', '6', '7' },
+  { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+  { '9' }
+};
+
+static void test_const_global_arrays (void)
+{
+  A (ca[0], 5);
+  A (&ca[0][0], 5);
+  A (&ca[0][1], 4);
+  A (&ca[0][3], 2);
+
+  int i = 0;
+  A (ca[i], 5);
+  A (&ca[i][0], 5);
+  A (&ca[i][1], 4);
+  A (&ca[i][3], 2);
+
+  int j = i;
+  A (&ca[i][i], 5);
+  A (&ca[i][j + 1], 4);
+  A (&ca[i][j + 2], 3);
+
+  A (&ca[idx][i], 5);
+  A (&ca[idx][j + 1], 4);
+  A (&ca[idx][j + 2], 3);
+
+  A (&ca[idx][idx], 5);
+  A (&ca[idx][idx + 1], 4);
+  A (&ca[idx][idx + 2], 3);
+
+  A (&ca[0][++j], 4);
+  A (&ca[0][++j], 3);
+  A (&ca[0][++j], 2);
+
+  if (j != 3)
+    ++nfails;
+}
+
+
+static void test_const_local_arrays (void)
+{
+  const char a[][4] = {
+    { '1', '2', '3', '4' }, { '5' },
+    { '1', '2', '3', '4' }, { '5', '6' },
+    { '1', '2', '3', '4' }, { '5', '6', '7' },
+    { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+    { '9' }
+  };
+
+  A (a[0], 5);
+  A (&a[0][0], 5);
+  A (&a[0][1], 4);
+  A (&a[0][3], 2);
+
+  int i = 0;
+  A (a[i], 5);
+  A (&a[i][0], 5);
+  A (&a[i][1], 4);
+  A (&a[i][3], 2);
+
+  int j = i;
+  A (&a[i][i], 5);
+  A (&a[i][j + 1], 4);
+  A (&a[i][j + 2], 3);
+
+  A (&a[idx][i], 5);
+  A (&a[idx][j + 1], 4);
+  A (&a[idx][j + 2], 3);
+
+  A (&a[idx][idx], 5);
+  A (&a[idx][idx + 1], 4);
+  A (&a[idx][idx + 2], 3);
+
+  A (&a[0][++j], 4);
+  A (&a[0][++j], 3);
+  A (&a[0][++j], 2);
+
+  if (j != 3)
+    ++nfails;
+}
+
+
+char va[][4] = {
+  { '1', '2', '3', '4' }, { '5' },
+  { '1', '2', '3', '4' }, { '5', '6' },
+  { '1', '2', '3', '4' }, { '5', '6', '7' },
+  { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+  { '9' }
+};
+
+static void test_nonconst_global_arrays (void)
+{
+  {
+    A (va[0], 5);
+    A (&va[0][0], 5);
+    A (&va[0][1], 4);
+    A (&va[0][3], 2);
+
+    int i = 0;
+    A (va[i], 5);
+    A (&va[i][0], 5);
+    A (&va[i][1], 4);
+    A (&va[i][3], 2);
+
+    int j = i;
+    A (&va[i][i], 5);
+    A (&va[i][j + 1], 4);
+    A (&va[i][j + 2], 3);
+
+    A (&va[idx][i], 5);
+    A (&va[idx][j + 1], 4);
+    A (&va[idx][j + 2], 3);
+
+    A (&va[idx][idx], 5);
+    A (&va[idx][idx + 1], 4);
+    A (&va[idx][idx + 2], 3);
+  }
+
+  {
+    A (va[2], 6);
+    A (&va[2][0], 6);
+    A (&va[2][1], 5);
+    A (&va[2][3], 3);
+
+    int i = 2;
+    A (va[i], 6);
+    A (&va[i][0], 6);
+    A (&va[i][1], 5);
+    A (&va[i][3], 3);
+
+    int j = i - 1;
+    A (&va[i][j - 1], 6);
+    A (&va[i][j], 5);
+    A (&va[i][j + 1], 4);
+
+    A (&va[idx + 2][i - 1], 5);
+    A (&va[idx + 2][j], 5);
+    A (&va[idx + 2][j + 1], 4);
+  }
+
+  int j = 0;
+
+  A (&va[0][++j], 4);
+  A (&va[0][++j], 3);
+  A (&va[0][++j], 2);
+
+  if (j != 3)
+    ++nfails;
+}
+
+
+static void test_nonconst_local_arrays (void)
+{
+  char a[][4] = {
+    { '1', '2', '3', '4' }, { '5' },
+    { '1', '2', '3', '4' }, { '5', '6' },
+    { '1', '2', '3', '4' }, { '5', '6', '7' },
+    { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+    { '9' }
+  };
+
+  A (a[0], 5);
+  A (&a[0][0], 5);
+  A (&a[0][1], 4);
+  A (&a[0][3], 2);
+
+  int i = 0;
+  A (a[i], 5);
+  A (&a[i][0], 5);
+  A (&a[i][1], 4);
+  A (&a[i][3], 2);
+
+  int j = i;
+  A (&a[i][i], 5);
+  A (&a[i][j + 1], 4);
+  A (&a[i][j + 2], 3);
+
+  A (&a[idx][i], 5);
+  A (&a[idx][j + 1], 4);
+  A (&a[idx][j + 2], 3);
+
+  A (&a[idx][idx], 5);
+  A (&a[idx][idx + 1], 4);
+  A (&a[idx][idx + 2], 3);
+
+  A (&a[0][++j], 4);
+  A (&a[0][++j], 3);
+  A (&a[0][++j], 2);
+
+  if (j != 3)
+    ++nfails;
+}
+
+
+struct MemArrays { char a[4], b[4]; };
+
+const struct MemArrays cma[] = {
+  { { '1', '2', '3', '4' }, { '5' } },
+  { { '1', '2', '3', '4' }, { '5', '6' } },
+  { { '1', '2', '3', '4' }, { '5', '6' } },
+  { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+  { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+  { { '9' }, { '\0' } }
+};
+
+static void test_const_global_member_arrays (void)
+{
+  {
+    A (cma[0].a, 5);
+    A (&cma[0].a[0], 5);
+    A (&cma[0].a[1], 4);
+    A (&cma[0].a[2], 3);
+
+    int i = 0;
+    A (cma[i].a, 5);
+    A (&cma[i].a[0], 5);
+    A (&cma[i].a[1], 4);
+    A (&cma[i].a[2], 3);
+
+    int j = i;
+    A (&cma[i].a[j], 5);
+    A (&cma[i].a[j + 1], 4);
+    A (&cma[i].a[j + 2], 3);
+
+    A (&cma[idx].a[i], 5);
+    A (&cma[idx].a[j + 1], 4);
+    A (&cma[idx].a[j + 2], 3);
+
+    A (&cma[idx].a[idx], 5);
+    A (&cma[idx].a[idx + 1], 4);
+    A (&cma[idx].a[idx + 2], 3);
+  }
+
+  {
+    A (cma[1].a, 6);
+    A (&cma[1].a[0], 6);
+    A (&cma[1].a[1], 5);
+    A (&cma[1].a[2], 4);
+
+    int i = 1;
+    A (cma[i].a, 6);
+    A (&cma[i].a[0], 6);
+    A (&cma[i].a[1], 5);
+    A (&cma[i].a[2], 4);
+
+    int j = i - 1;
+    A (&cma[i].a[j], 6);
+    A (&cma[i].a[j + 1], 5);
+    A (&cma[i].a[j + 2], 4);
+
+    A (&cma[idx + 1].a[j], 6);
+    A (&cma[idx + 1].a[j + 1], 5);
+    A (&cma[idx + 1].a[j + 2], 4);
+
+    A (&cma[idx + 1].a[idx], 6);
+    A (&cma[idx + 1].a[idx + 1], 5);
+    A (&cma[idx + 1].a[idx + 2], 4);
+  }
+
+  {
+    A (cma[4].a, 9);
+    A (&cma[4].a[0], 9);
+    A (&cma[4].a[1], 8);
+    A (&cma[4].b[0], 5);
+
+    int i = 4;
+    A (cma[i].a, 9);
+    A (&cma[i].a[0], 9);
+    A (&cma[i].a[1], 8);
+    A (&cma[i].b[0], 5);
+
+    int j = i - 1;
+    A (&cma[i].a[j], 6);
+    A (&cma[i].a[j + 1], 5);
+    A (&cma[i].b[j - 2], 4);
+
+    A (&cma[idx + 4].a[j], 6);
+    A (&cma[idx + 4].a[j + 1], 5);
+    A (&cma[idx + 4].b[j - 2], 4);
+
+    A (&cma[idx + 4].a[idx], 9);
+    A (&cma[idx + 4].a[idx + 1], 8);
+    A (&cma[idx + 4].b[idx + 1], 4);
+  }
+}
+
+
+static void test_const_local_member_arrays (void)
+{
+  const struct MemArrays ma[] = {
+    { { '1', '2', '3', '4' }, { '5' } },
+    { { '1', '2', '3', '4' }, { '5', '6' } },
+    { { '1', '2', '3', '4' }, { '5', '6' } },
+    { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+    { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+    { { '9' }, { '\0' } }
+  };
+
+  {
+    A (ma[0].a, 5);
+    A (&ma[0].a[0], 5);
+    A (&ma[0].a[1], 4);
+    A (&ma[0].a[2], 3);
+
+    int i = 0;
+    A (ma[i].a, 5);
+    A (&ma[i].a[0], 5);
+    A (&ma[i].a[1], 4);
+    A (&ma[i].a[2], 3);
+
+    int j = i;
+    A (&ma[i].a[j], 5);
+    A (&ma[i].a[j + 1], 4);
+    A (&ma[i].a[j + 2], 3);
+
+    A (&ma[idx].a[i], 5);
+    A (&ma[idx].a[j + 1], 4);
+    A (&ma[idx].a[j + 2], 3);
+
+    A (&ma[idx].a[idx], 5);
+    A (&ma[idx].a[idx + 1], 4);
+    A (&ma[idx].a[idx + 2], 3);
+  }
+
+  {
+    A (ma[1].a, 6);
+    A (&ma[1].a[0], 6);
+    A (&ma[1].a[1], 5);
+    A (&ma[1].a[2], 4);
+
+    int i = 1;
+    A (ma[i].a, 6);
+    A (&ma[i].a[0], 6);
+    A (&ma[i].a[1], 5);
+    A (&ma[i].a[2], 4);
+
+    int j = i - 1;
+    A (&ma[i].a[j], 6);
+    A (&ma[i].a[j + 1], 5);
+    A (&ma[i].a[j + 2], 4);
+
+    A (&ma[idx + 1].a[j], 6);
+    A (&ma[idx + 1].a[j + 1], 5);
+    A (&ma[idx + 1].a[j + 2], 4);
+
+    A (&ma[idx + 1].a[idx], 6);
+    A (&ma[idx + 1].a[idx + 1], 5);
+    A (&ma[idx + 1].a[idx + 2], 4);
+  }
+
+  {
+    A (ma[4].a, 9);
+    A (&ma[4].a[0], 9);
+    A (&ma[4].a[1], 8);
+    A (&ma[4].b[0], 5);
+
+    int i = 4;
+    A (ma[i].a, 9);
+    A (&ma[i].a[0], 9);
+    A (&ma[i].a[1], 8);
+    A (&ma[i].b[0], 5);
+
+    int j = i - 1;
+    A (&ma[i].a[j], 6);
+    A (&ma[i].a[j + 1], 5);
+    A (&ma[i].b[j - 2], 4);
+
+    A (&ma[idx + 4].a[j], 6);
+    A (&ma[idx + 4].a[j + 1], 5);
+    A (&ma[idx + 4].b[j - 2], 4);
+
+    A (&ma[idx + 4].a[idx], 9);
+    A (&ma[idx + 4].a[idx + 1], 8);
+    A (&ma[idx + 4].b[idx + 1], 4);
+  }
+}
+
+struct MemArrays vma[] = {
+  { { '1', '2', '3', '4' }, { '5' } },
+  { { '1', '2', '3', '4' }, { '5', '6' } },
+  { { '1', '2', '3', '4' }, { '5', '6' } },
+  { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+  { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+  { { '9' }, { '\0' } }
+};
+
+static void test_nonconst_global_member_arrays (void)
+{
+  {
+    A (vma[0].a, 5);
+    A (&vma[0].a[0], 5);
+    A (&vma[0].a[1], 4);
+    A (&vma[0].a[2], 3);
+
+    int i = 0;
+    A (vma[i].a, 5);
+    A (&vma[i].a[0], 5);
+    A (&vma[i].a[1], 4);
+    A (&vma[i].a[2], 3);
+
+    int j = i;
+    A (&vma[i].a[j], 5);
+    A (&vma[i].a[j + 1], 4);
+    A (&vma[i].a[j + 2], 3);
+
+    A (&vma[idx].a[i], 5);
+    A (&vma[idx].a[j + 1], 4);
+    A (&vma[idx].a[j + 2], 3);
+
+    A (&vma[idx].a[idx], 5);
+    A (&vma[idx].a[idx + 1], 4);
+    A (&vma[idx].a[idx + 2], 3);
+  }
+
+  {
+    A (vma[1].a, 6);
+    A (&vma[1].a[0], 6);
+    A (&vma[1].a[1], 5);
+    A (&vma[1].a[2], 4);
+
+    int i = 1;
+    A (vma[i].a, 6);
+    A (&vma[i].a[0], 6);
+    A (&vma[i].a[1], 5);
+    A (&vma[i].a[2], 4);
+
+    int j = i - 1;
+    A (&vma[i].a[j], 6);
+    A (&vma[i].a[j + 1], 5);
+    A (&vma[i].a[j + 2], 4);
+
+    A (&vma[idx + 1].a[j], 6);
+    A (&vma[idx + 1].a[j + 1], 5);
+    A (&vma[idx + 1].a[j + 2], 4);
+
+    A (&vma[idx + 1].a[idx], 6);
+    A (&vma[idx + 1].a[idx + 1], 5);
+    A (&vma[idx + 1].a[idx + 2], 4);
+  }
+
+  {
+    A (vma[4].a, 9);
+    A (&vma[4].a[0], 9);
+    A (&vma[4].a[1], 8);
+    A (&vma[4].b[0], 5);
+
+    int i = 4;
+    A (vma[i].a, 9);
+    A (&vma[i].a[0], 9);
+    A (&vma[i].a[1], 8);
+    A (&vma[i].b[0], 5);
+
+    int j = i - 1;
+    A (&vma[i].a[j], 6);
+    A (&vma[i].a[j + 1], 5);
+    A (&vma[i].b[j - 2], 4);
+
+    A (&vma[idx + 4].a[j], 6);
+    A (&vma[idx + 4].a[j + 1], 5);
+    A (&vma[idx + 4].b[j - 2], 4);
+
+    A (&vma[idx + 4].a[idx], 9);
+    A (&vma[idx + 4].a[idx + 1], 8);
+    A (&vma[idx + 4].b[idx + 1], 4);
+  }
+}
+
+
+static void test_nonconst_local_member_arrays (void)
+{
+  struct MemArrays ma[] = {
+    { { '1', '2', '3', '4' }, { '5' } },
+    { { '1', '2', '3', '4' }, { '5', '6' } },
+    { { '1', '2', '3', '4' }, { '5', '6' } },
+    { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+    { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+    { { '9' }, { '\0' } }
+  };
+
+  {
+    A (ma[0].a, 5);
+    A (&ma[0].a[0], 5);
+    A (&ma[0].a[1], 4);
+    A (&ma[0].a[2], 3);
+
+    int i = 0;
+    A (ma[i].a, 5);
+    A (&ma[i].a[0], 5);
+    A (&ma[i].a[1], 4);
+    A (&ma[i].a[2], 3);
+
+    int j = i;
+    A (&ma[i].a[j], 5);
+    A (&ma[i].a[j + 1], 4);
+    A (&ma[i].a[j + 2], 3);
+
+    A (&ma[idx].a[i], 5);
+    A (&ma[idx].a[j + 1], 4);
+    A (&ma[idx].a[j + 2], 3);
+
+    A (&ma[idx].a[idx], 5);
+    A (&ma[idx].a[idx + 1], 4);
+    A (&ma[idx].a[idx + 2], 3);
+  }
+
+  {
+    A (ma[1].a, 6);
+    A (&ma[1].a[0], 6);
+    A (&ma[1].a[1], 5);
+    A (&ma[1].a[2], 4);
+
+    int i = 1;
+    A (ma[i].a, 6);
+    A (&ma[i].a[0], 6);
+    A (&ma[i].a[1], 5);
+    A (&ma[i].a[2], 4);
+
+    int j = i - 1;
+    A (&ma[i].a[j], 6);
+    A (&ma[i].a[j + 1], 5);
+    A (&ma[i].a[j + 2], 4);
+
+    A (&ma[idx + 1].a[j], 6);
+    A (&ma[idx + 1].a[j + 1], 5);
+    A (&ma[idx + 1].a[j + 2], 4);
+
+    A (&ma[idx + 1].a[idx], 6);
+    A (&ma[idx + 1].a[idx + 1], 5);
+    A (&ma[idx + 1].a[idx + 2], 4);
+  }
+
+  {
+    A (ma[4].a, 9);
+    A (&ma[4].a[0], 9);
+    A (&ma[4].a[1], 8);
+    A (&ma[4].b[0], 5);
+
+    int i = 4;
+    A (ma[i].a, 9);
+    A (&ma[i].a[0], 9);
+    A (&ma[i].a[1], 8);
+    A (&ma[i].b[0], 5);
+
+    int j = i - 1;
+    A (&ma[i].a[j], 6);
+    A (&ma[i].a[j + 1], 5);
+    A (&ma[i].b[j - 2], 4);
+
+    A (&ma[idx + 4].a[j], 6);
+    A (&ma[idx + 4].a[j + 1], 5);
+    A (&ma[idx + 4].b[j - 2], 4);
+
+    A (&ma[idx + 4].a[idx], 9);
+    A (&ma[idx + 4].a[idx + 1], 8);
+    A (&ma[idx + 4].b[idx + 1], 4);
+  }
+}
+
+
+union UnionMemberArrays
+{
+  struct { char a[4], b[4]; } a;
+  struct { char a[8]; } c;
+};
+
+const union UnionMemberArrays cu = {
+  { { '1', '2', '3', '4' }, { '5', } }
+};
+
+static void test_const_union_member_arrays (void)
+{
+  A (cu.a.a, 5);
+  A (cu.a.b, 1);
+  A (cu.c.a, 5);
+
+  const union UnionMemberArrays clu = {
+    { { '1', '2', '3', '4' }, { '5', '6' } }
+  };
+
+  A (clu.a.a, 6);
+  A (clu.a.b, 2);
+  A (clu.c.a, 6);
+}
+
+
+union UnionMemberArrays vu = {
+  { { '1', '2', '3', '4' }, { '5', '6' } }
+};
+
+static void test_nonconst_union_member_arrays (void)
+{
+  A (vu.a.a, 6);
+  A (vu.a.b, 2);
+  A (vu.c.a, 6);
+
+  union UnionMemberArrays lvu = {
+    { { '1', '2', '3', '4' }, { '5', '6', '7' } }
+  };
+
+  A (lvu.a.a, 7);
+  A (lvu.a.b, 3);
+  A (lvu.c.a, 7);
+}
+
+
+int main (void)
+{
+  test_const_global_arrays ();
+  test_const_local_arrays ();
+
+  test_nonconst_global_arrays ();
+  test_nonconst_local_arrays ();
+
+  test_const_global_member_arrays ();
+  test_const_local_member_arrays ();
+
+  test_nonconst_global_member_arrays ();
+  test_nonconst_local_member_arrays ();
+
+  test_const_union_member_arrays ();
+  test_nonconst_union_member_arrays ();
+
+  if (nfails)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-6.c b/gcc/testsuite/gcc.c-torture/execute/strlen-6.c
new file mode 100644
index 0000000..1df5b21
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/strlen-6.c
@@ -0,0 +1,113 @@
+/* Test to verify that strlen() calls with conditional expressions
+   and unterminated arrays or pointers to such things as arguments
+   are evaluated without making assumptions about array sizes.  */
+
+extern __SIZE_TYPE__ strlen (const char *);
+
+unsigned nfails;
+
+#define A(expr, N)						\
+  do {								\
+    const char *_s = (expr);					\
+    unsigned _n = strlen (_s);					\
+    ((_n == N)							\
+     ? 0							\
+     : (__builtin_printf ("line %i: strlen ((%s) = (\"%s\"))"	\
+			  " == %u failed\n",			\
+			  __LINE__, #expr, _s, N),		\
+	++nfails));						\
+  } while (0)
+
+
+volatile int i0 = 0;
+
+const char ca[2][3] = { "12" };
+const char cb[2][3] = { { '1', '2', '3', }, { '4' } };
+
+char va[2][3] = { "123" };
+char vb[2][3] = { { '1', '2', '3', }, { '4', '5' } };
+
+const char *s = "123456";
+
+
+static void test_binary_cond_expr_global (void)
+{
+  A (i0 ? "1" : ca[0], 2);
+  A (i0 ? ca[0] : "123", 3);
+
+  /* The call to strlen (cb[0]) is strictly undefined because the array
+     isn't nul-terminated.  This test verifies that the strlen range
+     optimization doesn't assume that the argument is necessarily nul
+     terminated.
+     Ditto for strlen (vb[0]).  */
+  A (i0 ? "1" : cb[0], 4);              /* GCC 8.2 failure */
+  A (i0 ? cb[0] : "12", 2);
+
+  A (i0 ? "1" : va[0], 3);              /* GCC 8.2 failure */
+  A (i0 ? va[0] : "1234", 4);
+
+  A (i0 ? "1" : vb[0], 5);              /* GCC 8.2 failure */
+  A (i0 ? vb[0] : "12", 2);
+}
+
+
+static void test_binary_cond_expr_local (void)
+{
+  const char lca[2][3] = { "12" };
+  const char lcb[2][3] = { { '1', '2', '3', }, { '4' } };
+
+  char lva[2][3] = { "123" };
+  char lvb[2][3] = { { '1', '2', '3', }, { '4', '5' } };
+
+  /* Also undefined as above.  */
+  A (i0 ? "1" : lca[0], 2);
+  A (i0 ? lca[0] : "123", 3);
+
+  A (i0 ? "1" : lcb[0], 4);             /* GCC 8.2 failure */
+  A (i0 ? lcb[0] : "12", 2);
+
+  A (i0 ? "1" : lva[0], 3);             /* GCC 8.2 failure */
+  A (i0 ? lva[0] : "1234", 4);
+
+  A (i0 ? "1" : lvb[0], 5);             /* GCC 8.2 failure */
+  A (i0 ? lvb[0] : "12", 2);
+}
+
+
+static void test_ternary_cond_expr (void)
+{
+  /* Also undefined.  */
+  A (i0 == 0 ? s : i0 == 1 ? vb[0] : "123", 6);
+  A (i0 == 0 ? vb[0] : i0 == 1 ? s : "123", 5);
+  A (i0 == 0 ? "123" : i0 == 1 ? s : vb[0], 3);
+}
+
+
+const char (*pca)[3] = &ca[0];
+const char (*pcb)[3] = &cb[0];
+
+char (*pva)[3] = &va[0];
+char (*pvb)[3] = &vb[0];
+
+static void test_binary_cond_expr_arrayptr (void)
+{
+  /* Also undefined.  */
+  A (i0 ? *pca : *pcb, 4);              /* GCC 8.2 failure */
+  A (i0 ? *pcb : *pca, 2);
+
+  A (i0 ? *pva : *pvb, 5);              /* GCC 8.2 failure */
+  A (i0 ? *pvb : *pva, 3);
+}
+
+
+int main (void)
+{
+  test_binary_cond_expr_global ();
+  test_binary_cond_expr_local ();
+
+  test_ternary_cond_expr ();
+  test_binary_cond_expr_arrayptr ();
+
+  if (nfails)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-7.c b/gcc/testsuite/gcc.c-torture/execute/strlen-7.c
new file mode 100644
index 0000000..884db65
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/strlen-7.c
@@ -0,0 +1,37 @@
+/* Test to verify that a strlen() call with a pointer to a dynamic type
+   doesn't make assumptions based on the static type of the original
+   pointer.  See g++.dg/init/strlen.C for the corresponding C++ test.  */
+
+struct A { int i; char a[1]; void (*p)(); };
+struct B { char a[sizeof (struct A) - __builtin_offsetof (struct A, a)]; };
+
+__attribute__ ((noipa)) void
+init (char *d, const char *s)
+{
+  __builtin_strcpy (d, s);
+}
+
+struct B b;
+
+__attribute__ ((noipa)) void
+test_dynamic_type (struct A *p)
+{
+  /* The following call is undefined because it writes past the end
+     of the p->a subobject, but the corresponding GIMPLE considers
+     it valid and there's apparently no way to distinguish invalid
+     cases from ones like it that might be valid.  If/when GIMPLE
+     changes to make this possible this test can be removed.  */
+  char *q = (char*)__builtin_memcpy (p->a, &b, sizeof b);
+
+  init (q, "foobar");
+
+  if (6 != __builtin_strlen (q))
+    __builtin_abort();
+}
+
+int main (void)
+{
+  struct A *p = (struct A*)__builtin_malloc (sizeof *p);
+  test_dynamic_type (p);
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/strlenopt-36.c b/gcc/testsuite/gcc.dg/strlenopt-36.c
index d6fcca2..56e59a4 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-36.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-36.c
@@ -9,23 +9,6 @@ extern char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
 extern char a0[0];   /* Intentionally not tested here.  */
 extern char ax[];    /* Same.  */
 
-struct MemArrays {
-  char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
-  char a0[0];   /* Not tested here.  */
-};
-
-struct NestedMemArrays {
-  struct { char a7[7]; } ma7;
-  struct { char a6[6]; } ma6;
-  struct { char a5[5]; } ma5;
-  struct { char a4[4]; } ma4;
-  struct { char a3[3]; } ma3;
-  struct { char a2[2]; } ma2;
-  struct { char a1[1]; } ma1;
-  struct { char a0[0]; } ma0;
-  char last;
-};
-
 extern void failure_on_line (int);
 
 #define TEST_FAIL(line)					\
@@ -51,36 +34,4 @@ void test_array (void)
     T (strlen (a1) == 0);  */
 }
 
-void test_memarray (struct MemArrays *ma)
-{
-  T (strlen (ma->a7) < sizeof ma->a7);
-  T (strlen (ma->a6) < sizeof ma->a6);
-  T (strlen (ma->a5) < sizeof ma->a5);
-  T (strlen (ma->a4) < sizeof ma->a4);
-  T (strlen (ma->a3) < sizeof ma->a3);
-
-  /* The following two calls are folded too early which defeats
-     the strlen() optimization.
-  T (strlen (ma->a2) == 1);
-  T (strlen (ma->a1) == 0);  */
-}
-
-/* Verify that the range of strlen(A) of a last struct member is
-   set even when the array is the sole member of a struct as long
-   as the struct itself is a member of another struct.  The converse
-   is tested in stlenopt-37.c.  */
-void test_nested_memarray (struct NestedMemArrays *ma)
-{
-  T (strlen (ma->ma7.a7) < sizeof ma->ma7.a7);
-  T (strlen (ma->ma6.a6) < sizeof ma->ma6.a6);
-  T (strlen (ma->ma5.a5) < sizeof ma->ma5.a5);
-  T (strlen (ma->ma4.a4) < sizeof ma->ma4.a4);
-  T (strlen (ma->ma3.a3) < sizeof ma->ma3.a3);
-
-  /* The following two calls are folded too early which defeats
-     the strlen() optimization.
-  T (strlen (ma->ma2.a2) == 1);
-  T (strlen (ma->ma1.a1) == 0);  */
-}
-
 /* { dg-final { scan-tree-dump-not "failure_on_line" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-40.c b/gcc/testsuite/gcc.dg/strlenopt-40.c
index f4577d6..7a97ebb 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-40.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-40.c
@@ -105,46 +105,53 @@ void elim_global_arrays (int i)
   /* Verify that the expression involving the strlen call as well
      as whatever depends on it is eliminated  from the test output.
      All these expressions must be trivially true.  */
-  ELIM_TRUE (strlen (a7_3[0]) < sizeof a7_3[0]);
-  ELIM_TRUE (strlen (a7_3[1]) < sizeof a7_3[1]);
-  ELIM_TRUE (strlen (a7_3[6]) < sizeof a7_3[6]);
-  ELIM_TRUE (strlen (a7_3[i]) < sizeof a7_3[i]);
-
-  ELIM_TRUE (strlen (a5_7[0]) < sizeof a5_7[0]);
-  ELIM_TRUE (strlen (a5_7[1]) < sizeof a5_7[1]);
-  ELIM_TRUE (strlen (a5_7[4]) < sizeof a5_7[4]);
-  ELIM_TRUE (strlen (a5_7[i]) < sizeof a5_7[0]);
-
-  ELIM_TRUE (strlen (ax_3[0]) < sizeof ax_3[0]);
-  ELIM_TRUE (strlen (ax_3[1]) < sizeof ax_3[1]);
-  ELIM_TRUE (strlen (ax_3[9]) < sizeof ax_3[9]);
-  ELIM_TRUE (strlen (ax_3[i]) < sizeof ax_3[i]);
+  ELIM_TRUE (strlen (a7_3[0]) < sizeof a7_3);
+  ELIM_TRUE (strlen (a7_3[1]) < sizeof a7_3 - sizeof *a7_3);
+  ELIM_TRUE (strlen (a7_3[6]) < sizeof a7_3 - 5 * sizeof *a7_3);
+  ELIM_TRUE (strlen (a7_3[i]) < sizeof a7_3);
+
+  ELIM_TRUE (strlen (a5_7[0]) < sizeof a5_7);
+  ELIM_TRUE (strlen (a5_7[1]) < sizeof a5_7 - sizeof *a5_7);
+  ELIM_TRUE (strlen (a5_7[4]) < sizeof a5_7 - 3 * sizeof *a5_7);
+  ELIM_TRUE (strlen (a5_7[i]) < sizeof a5_7);
+
+  /* Even when treating a multi-dimensional array as a single string
+     the length must be less DIFF_MAX - (ax_3[i] - ax_3[0]) but GCC
+     doesn't do that computation yet so avoid testing it.  */
+  ELIM_TRUE (strlen (ax_3[0]) < DIFF_MAX);
+  ELIM_TRUE (strlen (ax_3[1]) < DIFF_MAX);
+  ELIM_TRUE (strlen (ax_3[9]) < DIFF_MAX);
+  ELIM_TRUE (strlen (ax_3[i]) < DIFF_MAX);
 
   ELIM_TRUE (strlen (a3) < sizeof a3);
   ELIM_TRUE (strlen (a7) < sizeof a7);
 
   ELIM_TRUE (strlen (ax) != DIFF_MAX);
-  ELIM_TRUE (strlen (ax) != DIFF_MAX - 1);
-  ELIM_TRUE (strlen (ax) < DIFF_MAX - 1);
+  /* ELIM_TRUE (strlen (ax) != DIFF_MAX - 1); */
+  /* ELIM_TRUE (strlen (ax) < DIFF_MAX - 1); */
 }
 
 void elim_pointer_to_arrays (void)
 {
-  ELIM_TRUE (strlen (*pa7) < 7);
-  ELIM_TRUE (strlen (*pa5) < 5);
-  ELIM_TRUE (strlen (*pa3) < 3);
-
-  ELIM_TRUE (strlen ((*pa7_3)[0]) < 3);
-  ELIM_TRUE (strlen ((*pa7_3)[1]) < 3);
-  ELIM_TRUE (strlen ((*pa7_3)[6]) < 3);
-
-  ELIM_TRUE (strlen ((*pax_3)[0]) < 3);
-  ELIM_TRUE (strlen ((*pax_3)[1]) < 3);
-  ELIM_TRUE (strlen ((*pax_3)[9]) < 3);
-
-  ELIM_TRUE (strlen ((*pa5_7)[0]) < 7);
-  ELIM_TRUE (strlen ((*pa5_7)[1]) < 7);
-  ELIM_TRUE (strlen ((*pa5_7)[4]) < 7);
+  /* Unfortunately, GCC cannot be trusted not to misuse a pointer
+     to a smaller array to point to an object of a bigger type so
+     the strlen range optimization must assume each array pointer
+     points effectively to an array of an unknown bound.  */
+  ELIM_TRUE (strlen (*pa7) < DIFF_MAX);
+  ELIM_TRUE (strlen (*pa5) < DIFF_MAX);
+  ELIM_TRUE (strlen (*pa3) < DIFF_MAX);
+
+  ELIM_TRUE (strlen ((*pa7_3)[0]) < DIFF_MAX);
+  ELIM_TRUE (strlen ((*pa7_3)[1]) < DIFF_MAX);
+  ELIM_TRUE (strlen ((*pa7_3)[6]) < DIFF_MAX);
+
+  ELIM_TRUE (strlen ((*pax_3)[0]) < DIFF_MAX);
+  ELIM_TRUE (strlen ((*pax_3)[1]) < DIFF_MAX);
+  ELIM_TRUE (strlen ((*pax_3)[9]) < DIFF_MAX);
+
+  ELIM_TRUE (strlen ((*pa5_7)[0]) < DIFF_MAX);
+  ELIM_TRUE (strlen ((*pa5_7)[1]) < DIFF_MAX);
+  ELIM_TRUE (strlen ((*pa5_7)[4]) < DIFF_MAX);
 }
 
 void elim_global_arrays_and_strings (int i)
@@ -176,65 +183,33 @@ void elim_global_arrays_and_strings (int i)
 
 void elim_member_arrays_obj (int i)
 {
-  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a3) < 3);
-  ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a3) < 3);
-  ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a3) < 3);
-  ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a3) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a3) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a3) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a3) < sizeof ma0_3_5_7);
 
-  ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a3) < 3);
-  ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a3) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a3) < sizeof ma0_3_5_7);
 
-  ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a3) < 3);
-  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a3) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a3) < sizeof ma0_3_5_7);
 
-  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5) < 5);
-  ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a5) < 5);
-  ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a5) < 5);
-  ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a5) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a5) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a5) < sizeof ma0_3_5_7);
 
-  ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a5) < 5);
-  ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a5) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a5) < sizeof ma0_3_5_7);
 
-  ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a5) < 5);
-  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a5) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5) < sizeof ma0_3_5_7);
 
-  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < 3);
-  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < sizeof ma0_3_5_7);
 
-  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 7);
-  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 7);
-}
-
-void elim_member_arrays_ptr (struct MemArrays0 *ma0,
-			     struct MemArraysX *max,
-			     struct MemArrays7 *ma7,
-			     int i)
-{
-  ELIM_TRUE (strlen (ma0->a7_3[0]) < 3);
-  ELIM_TRUE (strlen (ma0->a7_3[1]) < 3);
-  ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
-  ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
-  ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
-  ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
-
-  ELIM_TRUE (strlen (ma0->a5_7[0]) < 7);
-  ELIM_TRUE (strlen (ma0[0].a5_7[0]) < 7);
-  ELIM_TRUE (strlen (ma0[1].a5_7[0]) < 7);
-  ELIM_TRUE (strlen (ma0[1].a5_7[4]) < 7);
-  ELIM_TRUE (strlen (ma0[9].a5_7[0]) < 7);
-  ELIM_TRUE (strlen (ma0[9].a5_7[4]) < 7);
-
-  ELIM_TRUE (strlen (ma0->a3) < sizeof ma0->a3);
-  ELIM_TRUE (strlen (ma0->a5) < sizeof ma0->a5);
-  ELIM_TRUE (strlen (ma0->a0) < DIFF_MAX - 1);
-
-  ELIM_TRUE (strlen (max->a3) < sizeof max->a3);
-  ELIM_TRUE (strlen (max->a5) < sizeof max->a5);
-  ELIM_TRUE (strlen (max->ax) < DIFF_MAX - 1);
-
-  ELIM_TRUE (strlen (ma7->a3) < sizeof max->a3);
-  ELIM_TRUE (strlen (ma7->a5) < sizeof max->a5);
-  ELIM_TRUE (strlen (ma7->a7) < DIFF_MAX - 1);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < sizeof ma0_3_5_7);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < sizeof ma0_3_5_7);
 }
 
 
@@ -255,11 +230,27 @@ void keep_global_arrays (int i)
   KEEP (strlen (a5_7[4]) < 6);
   KEEP (strlen (a5_7[i]) < 6);
 
+  /* Verify also that tests (and strlen calls) are not eliminated
+     for results greater than what would the size of the innermost
+     array suggest might be possible (in case the element array is
+     not nul-terminated), even though such calls are undefined.  */
+  KEEP (strlen (a5_7[0]) > sizeof a5_7 - 2);
+  KEEP (strlen (a5_7[1]) > sizeof a5_7 - sizeof a5_7[1] - 2);
+  KEEP (strlen (a5_7[i]) > sizeof a5_7 - 2);
+
   KEEP (strlen (ax_3[0]) < 2);
   KEEP (strlen (ax_3[1]) < 2);
   KEEP (strlen (ax_3[2]) < 2);
   KEEP (strlen (ax_3[i]) < 2);
 
+  /* Here again, verify that the ax_3 matrix is treated essentially
+     as a flat array of unknown bound for the benefit of all the
+     undefined code out there that might rely on it.  */
+  KEEP (strlen (ax_3[0]) > 3);
+  KEEP (strlen (ax_3[1]) > 9);
+  KEEP (strlen (ax_3[2]) > 99);
+  KEEP (strlen (ax_3[i]) > 999);
+
   KEEP (strlen (a3) < 2);
   KEEP (strlen (a7) < 6);
 
@@ -274,24 +265,48 @@ void keep_global_arrays (int i)
   KEEP (strlen (ax) < 1);
 }
 
-void keep_pointer_to_arrays (void)
+void keep_pointer_to_arrays (int i)
 {
   KEEP (strlen (*pa7) < 6);
   KEEP (strlen (*pa5) < 4);
   KEEP (strlen (*pa3) < 2);
 
+  /* Since GCC cannot be trusted not to misuse a pointer to a smaller
+     array to point to an object of a larger type verify that the bound
+     in a pointer to an array of a known bound isn't relied on for
+     the strlen range optimization.  If GCC is fixed to avoid these
+     misuses these tests can be removed.  */
+  KEEP (strlen (*pa7) > sizeof *pa7);
+  KEEP (strlen (*pa5) > sizeof *pa5);
+  KEEP (strlen (*pa3) > sizeof *pa3);
+
   KEEP (strlen ((*pa7_3)[0]) < 2);
   KEEP (strlen ((*pa7_3)[1]) < 2);
   KEEP (strlen ((*pa7_3)[6]) < 2);
+  KEEP (strlen ((*pa7_3)[i]) < 2);
+
+  /* Same as above.  */
+  KEEP (strlen ((*pa7_3)[0]) > sizeof *pa7_3);
+  KEEP (strlen ((*pa7_3)[i]) > sizeof *pa7_3);
 
   KEEP (strlen ((*pax_3)[0]) < 2);
   KEEP (strlen ((*pax_3)[1]) < 2);
   KEEP (strlen ((*pax_3)[9]) < 2);
+  KEEP (strlen ((*pax_3)[i]) < 2);
+
+  /* Same as above.  */
+  KEEP (strlen ((*pax_3)[0]) > 3);
+  KEEP (strlen ((*pax_3)[i]) > 333);
 
   KEEP (strlen ((*pa5_7)[0]) < 6);
   KEEP (strlen ((*pa5_7)[1]) < 6);
   KEEP (strlen ((*pa5_7)[4]) < 6);
-}
+  KEEP (strlen ((*pa5_7)[i]) < 6);
+
+  /* Same as above.  */
+  KEEP (strlen ((*pa5_7)[0]) > sizeof *pa5_7);
+  KEEP (strlen ((*pa5_7)[i]) > sizeof *pa5_7);
+ }
 
 void keep_global_arrays_and_strings (int i)
 {
@@ -306,6 +321,12 @@ void keep_global_arrays_and_strings (int i)
   KEEP (strlen (i < 0 ? a7 : "123") < 5);
   KEEP (strlen (i < 0 ? a7 : "123456") < 6);
   KEEP (strlen (i < 0 ? a7 : "1234567") < 6);
+
+  /* Verify that a matrix is treated as a flat array even in a conditional
+     expression (i.e., don't assume that a7_3[0] is nul-terminated, even
+     though calling strlen() on such an array is undefined).  */
+  KEEP (strlen (i < 0 ? a7_3[0] : "") > 7);
+  KEEP (strlen (i < 0 ? a7_3[i] : "") > 7);
 }
 
 void keep_member_arrays_obj (int i)
@@ -337,6 +358,12 @@ void keep_member_arrays_obj (int i)
 
   KEEP (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 6);
   KEEP (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 6);
+
+  /* Again, verify that the .a3 array isn't assumed to necessarily
+     be nul-terminated.  */
+  KEEP (strlen (ma0_3_5_7[0][0][0].a3) > 2);
+  KEEP (strlen (ma0_3_5_7[0][0][6].a3) > 2);
+  KEEP (strlen (ma0_3_5_7[0][0][i].a3) > 2);
 }
 
 void keep_member_arrays_ptr (struct MemArrays0 *ma0,
@@ -353,6 +380,11 @@ void keep_member_arrays_ptr (struct MemArrays0 *ma0,
   KEEP (strlen (ma0->a7_3[i]) < 2);
   KEEP (strlen (ma0->a7_3[i]) < 2);
 
+  /* Again, verify that the member array isn't assumed to necessarily
+     be nul-terminated.  */
+  KEEP (strlen (ma0->a7_3[0]) > sizeof ma0->a7_3);
+  KEEP (strlen (ma0->a7_3[i]) > sizeof ma0->a7_3);
+
   KEEP (strlen (ma0->a5_7[0]) < 5);
   KEEP (strlen (ma0[0].a5_7[0]) < 5);
   KEEP (strlen (ma0[1].a5_7[0]) < 5);
@@ -361,6 +393,9 @@ void keep_member_arrays_ptr (struct MemArrays0 *ma0,
   KEEP (strlen (ma0[i].a5_7[4]) < 5);
   KEEP (strlen (ma0[i].a5_7[i]) < 5);
 
+  /* Same as above.  */
+  KEEP (strlen (ma0[i].a5_7[i]) > sizeof ma0[i].a5_7);
+
   KEEP (strlen (ma0->a0) < DIFF_MAX - 2);
   KEEP (strlen (ma0->a0) < 999);
   KEEP (strlen (ma0->a0) < 1);
@@ -389,5 +424,5 @@ void keep_pointers (const char *s)
 /* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } }
    { dg-final { scan-tree-dump-times "call_in_false_branch_not_eliminated_" 0 "optimized" } }
 
-   { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 92 "optimized" } }
-   { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 92 "optimized" } } */
+   { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 119 "optimized" } }
+   { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 119 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-45.c b/gcc/testsuite/gcc.dg/strlenopt-45.c
index bd9b197..31c1e53 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-45.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-45.c
@@ -2,7 +2,7 @@
    Test to verify that strnlen built-in expansion works correctly
    in the absence of tree strlen optimization.
    { dg-do compile }
-   { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+   { dg-options "-O2 -Wall -Wno-stringop-overflow -fdump-tree-optimized" } */
 
 #include "strlenopt.h"
 
@@ -85,19 +85,19 @@ void elim_strnlen_arr_cst (void)
   ELIM (strnlen (a3_7[0], 1) < 2);
   ELIM (strnlen (a3_7[0], 2) < 3);
   ELIM (strnlen (a3_7[0], 3) < 4);
-  ELIM (strnlen (a3_7[0], 9) < 8);
-  ELIM (strnlen (a3_7[0], PTRDIFF_MAX) < 8);
-  ELIM (strnlen (a3_7[0], SIZE_MAX) < 8);
-  ELIM (strnlen (a3_7[0], -1) < 8);
+  ELIM (strnlen (a3_7[0], 9) <= 9);
+  ELIM (strnlen (a3_7[0], PTRDIFF_MAX) <= sizeof a3_7);
+  ELIM (strnlen (a3_7[0], SIZE_MAX) <= sizeof a3_7);
+  ELIM (strnlen (a3_7[0], -1) <= sizeof a3_7);
 
   ELIM (strnlen (a3_7[2], 0) == 0);
   ELIM (strnlen (a3_7[2], 1) < 2);
   ELIM (strnlen (a3_7[2], 2) < 3);
   ELIM (strnlen (a3_7[2], 3) < 4);
-  ELIM (strnlen (a3_7[2], 9) < 8);
-  ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < 8);
-  ELIM (strnlen (a3_7[2], SIZE_MAX) < 8);
-  ELIM (strnlen (a3_7[2], -1) < 8);
+  ELIM (strnlen (a3_7[2], 9) <= 9);
+  ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < sizeof a3_7);
+  ELIM (strnlen (a3_7[2], SIZE_MAX) < sizeof a3_7);
+  ELIM (strnlen (a3_7[2], -1) < sizeof a3_7);
 
   ELIM (strnlen ((char*)a3_7, 0) == 0);
   ELIM (strnlen ((char*)a3_7, 1) < 2);
@@ -105,123 +105,19 @@ void elim_strnlen_arr_cst (void)
   ELIM (strnlen ((char*)a3_7, 3) < 4);
   ELIM (strnlen ((char*)a3_7, 9) < 10);
   ELIM (strnlen ((char*)a3_7, 19) < 20);
-  ELIM (strnlen ((char*)a3_7, 21) < 22);
-  ELIM (strnlen ((char*)a3_7, 23) < 22);
-  ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) < 22);
-  ELIM (strnlen ((char*)a3_7, SIZE_MAX) < 22);
-  ELIM (strnlen ((char*)a3_7, -1) < 22);
+  ELIM (strnlen ((char*)a3_7, 21) <= sizeof a3_7);
+  ELIM (strnlen ((char*)a3_7, 23) <= sizeof a3_7);
+  ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) <= sizeof a3_7);
+  ELIM (strnlen ((char*)a3_7, SIZE_MAX) <= sizeof a3_7);
+  ELIM (strnlen ((char*)a3_7, -1) <= sizeof a3_7);
 
   ELIM (strnlen (ax, 0) == 0);
   ELIM (strnlen (ax, 1) < 2);
   ELIM (strnlen (ax, 2) < 3);
   ELIM (strnlen (ax, 9) < 10);
-  ELIM (strnlen (a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
-  ELIM (strnlen (a3, SIZE_MAX) < PTRDIFF_MAX);
-  ELIM (strnlen (a3, -1) < PTRDIFF_MAX);
-}
-
-struct MemArrays
-{
-  char c;
-  char a0[0];
-  char a1[1];
-  char a3[3];
-  char a5[5];
-  char a3_7[3][7];
-  char ax[1];
-};
-
-void elim_strnlen_memarr_cst (struct MemArrays *p, int i)
-{
-  ELIM (strnlen (&p->c, 0) == 0);
-  ELIM (strnlen (&p->c, 1) < 2);
-  ELIM (strnlen (&p->c, 9) == 0);
-  ELIM (strnlen (&p->c, PTRDIFF_MAX) == 0);
-  ELIM (strnlen (&p->c, SIZE_MAX) == 0);
-  ELIM (strnlen (&p->c, -1) == 0);
-
-  /* Other accesses to internal zero-length arrays are undefined.  */
-  ELIM (strnlen (p->a0, 0) == 0);
-
-  ELIM (strnlen (p->a1, 0) == 0);
-  ELIM (strnlen (p->a1, 1) < 2);
-  ELIM (strnlen (p->a1, 9) == 0);
-  ELIM (strnlen (p->a1, PTRDIFF_MAX) == 0);
-  ELIM (strnlen (p->a1, SIZE_MAX) == 0);
-  ELIM (strnlen (p->a1, -1) == 0);
-
-  ELIM (strnlen (p->a3, 0) == 0);
-  ELIM (strnlen (p->a3, 1) < 2);
-  ELIM (strnlen (p->a3, 2) < 3);
-  ELIM (strnlen (p->a3, 3) < 4);
-  ELIM (strnlen (p->a3, 9) < 4);
-  ELIM (strnlen (p->a3, PTRDIFF_MAX) < 4);
-  ELIM (strnlen (p->a3, SIZE_MAX) < 4);
-  ELIM (strnlen (p->a3, -1) < 4);
-
-  ELIM (strnlen (p[i].a3, 0) == 0);
-  ELIM (strnlen (p[i].a3, 1) < 2);
-  ELIM (strnlen (p[i].a3, 2) < 3);
-  ELIM (strnlen (p[i].a3, 3) < 4);
-  ELIM (strnlen (p[i].a3, 9) < 4);
-  ELIM (strnlen (p[i].a3, PTRDIFF_MAX) < 4);
-  ELIM (strnlen (p[i].a3, SIZE_MAX) < 4);
-  ELIM (strnlen (p[i].a3, -1) < 4);
-
-  ELIM (strnlen (p->a3_7[0], 0) == 0);
-  ELIM (strnlen (p->a3_7[0], 1) < 2);
-  ELIM (strnlen (p->a3_7[0], 2) < 3);
-  ELIM (strnlen (p->a3_7[0], 3) < 4);
-  ELIM (strnlen (p->a3_7[0], 9) < 8);
-  ELIM (strnlen (p->a3_7[0], PTRDIFF_MAX) < 8);
-  ELIM (strnlen (p->a3_7[0], SIZE_MAX) < 8);
-  ELIM (strnlen (p->a3_7[0], -1) < 8);
-
-  ELIM (strnlen (p->a3_7[2], 0) == 0);
-  ELIM (strnlen (p->a3_7[2], 1) < 2);
-  ELIM (strnlen (p->a3_7[2], 2) < 3);
-  ELIM (strnlen (p->a3_7[2], 3) < 4);
-  ELIM (strnlen (p->a3_7[2], 9) < 8);
-  ELIM (strnlen (p->a3_7[2], PTRDIFF_MAX) < 8);
-  ELIM (strnlen (p->a3_7[2], SIZE_MAX) < 8);
-  ELIM (strnlen (p->a3_7[2], -1) < 8);
-
-  ELIM (strnlen (p->a3_7[i], 0) == 0);
-  ELIM (strnlen (p->a3_7[i], 1) < 2);
-  ELIM (strnlen (p->a3_7[i], 2) < 3);
-  ELIM (strnlen (p->a3_7[i], 3) < 4);
-
-#if 0
-  /* This is tranformed into strnlen ((char*)p + offsetof (a3_7[i]), N)
-     which makes it impssible to determine the size of the array.  */
-  ELIM (strnlen (p->a3_7[i], 9) < 8);
-  ELIM (strnlen (p->a3_7[i], PTRDIFF_MAX) < 8);
-  ELIM (strnlen (p->a3_7[i], SIZE_MAX) < 8);
-  ELIM (strnlen (p->a3_7[i], -1) < 8);
-#else
-  ELIM (strnlen (p->a3_7[i], 9) < 10);
-  ELIM (strnlen (p->a3_7[i], 19) < 20);
-#endif
-
-  ELIM (strnlen ((char*)p->a3_7, 0) == 0);
-  ELIM (strnlen ((char*)p->a3_7, 1) < 2);
-  ELIM (strnlen ((char*)p->a3_7, 2) < 3);
-  ELIM (strnlen ((char*)p->a3_7, 3) < 4);
-  ELIM (strnlen ((char*)p->a3_7, 9) < 10);
-  ELIM (strnlen ((char*)p->a3_7, 19) < 20);
-  ELIM (strnlen ((char*)p->a3_7, 21) < 22);
-  ELIM (strnlen ((char*)p->a3_7, 23) < 22);
-  ELIM (strnlen ((char*)p->a3_7, PTRDIFF_MAX) < 22);
-  ELIM (strnlen ((char*)p->a3_7, SIZE_MAX) < 22);
-  ELIM (strnlen ((char*)p->a3_7, -1) < 22);
-
-  ELIM (strnlen (p->ax, 0) == 0);
-  ELIM (strnlen (p->ax, 1) < 2);
-  ELIM (strnlen (p->ax, 2) < 3);
-  ELIM (strnlen (p->ax, 9) < 10);
-  ELIM (strnlen (p->a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
-  ELIM (strnlen (p->a3, SIZE_MAX) < PTRDIFF_MAX);
-  ELIM (strnlen (p->a3, -1) < PTRDIFF_MAX);
+  ELIM (strnlen (ax, PTRDIFF_MAX) < PTRDIFF_MAX);
+  ELIM (strnlen (ax, SIZE_MAX) < PTRDIFF_MAX);
+  ELIM (strnlen (ax, -1) < PTRDIFF_MAX);
 }
 
 
diff --git a/gcc/testsuite/gcc.dg/strlenopt-48.c b/gcc/testsuite/gcc.dg/strlenopt-48.c
index 39bb32d..179edd8 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-48.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-48.c
@@ -11,7 +11,7 @@ void f (void)
 {
   extern char a[2][1];
   int n = strlen (a[1]);
-  if (n)
+  if (n >= sizeof a)
     abort();
 }
 
@@ -19,7 +19,7 @@ void g (void)
 {
   extern char b[3][2][1];
   int n = strlen (b[2][1]);
-  if (n)
+  if (n >= sizeof b)
     abort();
 }
 
@@ -27,7 +27,7 @@ void h (void)
 {
   extern char c[4][3][2][1];
   int n = strlen (c[3][2][1]);
-  if (n)
+  if (n >= sizeof c)
     abort();
 }
 
diff --git a/gcc/testsuite/gcc.dg/strlenopt-51.c b/gcc/testsuite/gcc.dg/strlenopt-51.c
index cbed11b..3d879f1 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-51.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-51.c
@@ -1,16 +1,17 @@
 /* PR tree-optimization/77357 - strlen of constant strings not folded
    { dg-do compile }
-   { dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-optimized" } */
+   { dg-options "-O0 -Wall -fdump-tree-gimple" } */
 
 #include "strlenopt.h"
 
 #define CONCAT(x, y) x ## y
 #define CAT(x, y) CONCAT (x, y)
-#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__)
+#define FAILNAME(name, counter) \
+  CAT (CAT (CAT (call_ ## name ##_on_line_, __LINE__), _), counter)
 
-#define FAIL(name) do {				\
-    extern void FAILNAME (name) (void);		\
-    FAILNAME (name)();				\
+#define FAIL(name, counter) do {			\
+    extern void FAILNAME (name, counter) (void);	\
+    FAILNAME (name, counter)();				\
   } while (0)
 
 /* Macro to emit a call to funcation named
@@ -19,19 +20,7 @@
    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); 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 KEEP(expr)				\
-  if (expr)					\
-    FAIL (made_in_true_branch);			\
-  else						\
-    FAIL (made_in_false_branch)
+  if (!(expr)) FAIL (in_true_branch_not_eliminated, __COUNTER__); else (void)0
 
 #define T(s, n) ELIM (strlen (s) == n)
 
@@ -53,7 +42,7 @@ struct S
 
 const char a9[][9] = { S0, S1, S2, S3, S4, S5, S6, S7, S8 };
 
-void test_elim_a9 (int i)
+void test_elim_a9 (unsigned i)
 {
   ELIM (strlen (&a9[0][i]) > 0);
   ELIM (strlen (&a9[1][i]) > 1);
@@ -75,10 +64,10 @@ const char a9_9[][9][9] = {
   { S5, S6, S7, S8, S0, S1, S2, S3, S4 },
   { S6, S7, S8, S0, S1, S2, S3, S4, S5 },
   { S7, S8, S0, S1, S2, S3, S4, S5, S6 },
-  { S8, S0, S2, S2, S3, S4, S5, S6, S7 }
+  { S8, S0, S1, S2, S3, S4, S5, S6, S7 }
 };
 
-void test_elim_a9_9 (int i)
+void test_elim_a9_9 (unsigned i)
 {
 #undef T
 #define T(I)					\
@@ -95,27 +84,4 @@ void test_elim_a9_9 (int i)
   T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8);
 }
 
-#line 1000
-
-void test_keep_a9_9 (int i)
-{
-#undef T
-#define T(I)					\
-  KEEP (strlen (&a9_9[i][I][0]) > (1 + I) % 9);	\
-  KEEP (strlen (&a9_9[i][I][1]) > (1 + I) % 9);	\
-  KEEP (strlen (&a9_9[i][I][2]) > (2 + I) % 9);	\
-  KEEP (strlen (&a9_9[i][I][3]) > (3 + I) % 9);	\
-  KEEP (strlen (&a9_9[i][I][4]) > (4 + I) % 9);	\
-  KEEP (strlen (&a9_9[i][I][5]) > (5 + I) % 9);	\
-  KEEP (strlen (&a9_9[i][I][6]) > (6 + I) % 9);	\
-  KEEP (strlen (&a9_9[i][I][7]) > (7 + I) % 9);	\
-  KEEP (strlen (&a9_9[i][I][8]) > (8 + I) % 9)
-
-  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" 72 "gimple" } }
-   { dg-final { scan-tree-dump-times "strlen" 63 "optimized" } }
-
-   { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 72 "optimized" } }
-   { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 81 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-59.c b/gcc/testsuite/gcc.dg/strlenopt-59.c
new file mode 100644
index 0000000..9bacf87
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-59.c
@@ -0,0 +1,73 @@
+/* Verify that strlen() calls with constant conditional expressions are
+   eliminated as expected.
+
+   { dg-do compile }
+   { dg-options "-O1 -fdump-tree-optimized" }  */
+
+extern void abort (void);
+extern __SIZE_TYPE__ strlen (const char*);
+
+
+#define CAT(x, y) x ## y
+#define CONCAT(x, y) CAT (x, y)
+#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do {				\
+    extern void FAILNAME (name) (void);		\
+    FAILNAME (name)();				\
+  } while (0)
+
+/* Macros to emit a call to funcation named
+     call_failed_to_be_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 (test_not_eliminated); else (void)0
+
+extern char a3[3];
+extern char a7[7];
+
+struct MemArrays { char a[7], b[9]; };
+
+struct MemArrays ma;
+
+void test_elim_condexpr (int i)
+{
+  ELIM (6 < strlen (i ? "" : "123456"));
+  ELIM (6 < strlen (i ? "123456" : ""));
+
+  ELIM (4 < strlen (i < 1 ? "a" : i == 1 ? "ab" : "abc"));
+
+  ELIM (3 < strlen (i ? "" : a3));
+  ELIM (3 < strlen (i ? a3 : "1"));
+
+  ELIM (6 < strlen (i ? "12" : a7));
+  ELIM (6 < strlen (i ? a7 : "123"));
+
+  ELIM (6 < strlen (i ? "1234" : a7));
+  ELIM (7 < strlen (i ? a7 : "1234567"));
+
+  ELIM (3 < strlen (i < 1 ? "a" : i == 1 ? "ab" : a3));
+  ELIM (3 < strlen (i < 1 ? "a" : i == 1 ? a3 : "abc"));
+  ELIM (3 < strlen (i < 1 ? a3 : i == 1 ? "a" : "abc"));
+
+  ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? "ab" : a7));
+  ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? a7 : "abc"));
+  ELIM (6 < strlen (i < 1 ? a7 : i == 1 ? "a" : "abc"));
+
+  ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? a7 : a3));
+  ELIM (6 < strlen (i < 1 ? a7 : i == 1 ? "a" : a3));
+
+  {
+    enum { maxlen = sizeof ma - 1 };
+    ELIM (maxlen < strlen (ma.a));
+  }
+
+  {
+    enum { maxlen = sizeof ma - __builtin_offsetof (struct MemArrays, b) - 1 };
+    ELIM (maxlen < strlen (ma.b));
+  }
+}
+
+/* { dg-final { scan-tree-dump-times "test_not_eliminated_" 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-4.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-4.c
new file mode 100644
index 0000000..ad9a99b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-4.c
@@ -0,0 +1,51 @@
+/*
+  { dg-do compile }
+  { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void abort (void);
+extern int snprintf (char*, size_t, const char*, ...);
+
+const char s0[] = "";
+const char s1[] = "a";
+const char s2[] = "ab";
+
+extern char ax[];
+extern const char* const ptr;
+
+#define CAT(x, y)      x ## y
+#define CONCAT(x, y)   CAT (x, y)
+#define TEST           CONCAT (test_on_line_, __LINE__)
+
+#define KEEP(expr) do {				\
+    if ((expr))	{				\
+      extern void TEST (void);			\
+      TEST ();					\
+    }						\
+  } while (0)
+
+
+void test_literal (int i)
+{
+  KEEP (0 < snprintf (0, 0, "%s", i ? "" : ax));
+  KEEP (1 < snprintf (0, 0, "%s", i ? ax : "1"));
+  KEEP (2 < snprintf (0, 0, "%s", i ? "12" : ptr));
+
+  KEEP (1 > snprintf (0, 0, "%s", i ? "" : ax));
+  KEEP (1 > snprintf (0, 0, "%s", i ? ax : "1"));
+  KEEP (2 > snprintf (0, 0, "%s", i ? "12" : ptr));
+}
+
+void test_cststr (int i)
+{
+  KEEP (0 < snprintf (0, 0, "%s", i ? s0 : ax));
+  KEEP (1 < snprintf (0, 0, "%s", i ? ax : s1));
+  KEEP (2 < snprintf (0, 0, "%s", i ? s2 : ptr));
+
+  KEEP (1 > snprintf (0, 0, "%s", i ? s0 : ax));
+  KEEP (1 > snprintf (0, 0, "%s", i ? ax : s1));
+  KEEP (2 > snprintf (0, 0, "%s", i ? s2 : ptr));
+}
+
+/* { dg-final { scan-tree-dump-times "test_on_line_" 12 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79376.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79376.c
index 01ecd17..466dcde 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr79376.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr79376.c
@@ -40,7 +40,18 @@ void test_arrays (int i, struct Arrays *a)
 
     int n = __builtin_snprintf (0, 0, "%-s", s);
 
-    ASSERT (0 <= n && n < 3);
+    /* Since it's undefined to pass an unterminated array to a %s
+       directive it would be valid to assume that S above is not
+       longer than sizeof (A->A3) but the optimization isn't done
+       because the GIMPLE representation of the %s argument isn't
+       suffficiently reliable not to confuse it for some other
+       array.  The argument length is therefore assumed to be in
+       the range [0, PTRDIFF_MAX - 2] and the sprintf result to be
+       as big as INT_MAX and possibly even negative if the function
+       were to fail due to a single directive resulting in more than
+       the 4,095 byte maximum required to be supported.
+       ASSERT (0 <= n && n < 3);
+    */
 
     ASSERT_MAYBE (0 == n);
     ASSERT_MAYBE (1 == n);
@@ -52,7 +63,7 @@ void test_arrays (int i, struct Arrays *a)
 
     int n = __builtin_snprintf (0, 0, "%-s", s);
 
-    ASSERT (0 <= n && n < 5);
+    /* ASSERT (0 <= n && n < 5); */
 
     ASSERT_MAYBE (0 == n);
     ASSERT_MAYBE (1 == n);
@@ -69,7 +80,7 @@ void test_string_and_array (int i, struct Arrays *a)
 
     int n = __builtin_snprintf (0, 0, "%-s", s);
 
-    ASSERT (0 <= n && n < 3);
+    /* ASSERT (0 <= n && n < 3); */
 
     ASSERT_MAYBE (0 == n);
     ASSERT_MAYBE (1 == n);
@@ -81,7 +92,7 @@ void test_string_and_array (int i, struct Arrays *a)
 
     int n = __builtin_snprintf (0, 0, "%-s", s);
 
-    ASSERT (0 <= n && n < 5);
+    /* ASSERT (0 <= n && n < 5); */
 
     ASSERT_MAYBE (0 == n);
     ASSERT_MAYBE (1 == n);
@@ -95,7 +106,7 @@ void test_string_and_array (int i, struct Arrays *a)
 
     int n = __builtin_snprintf (0, 0, "%-s", s);
 
-    ASSERT (0 <= n && n < 5);
+    /* ASSERT (0 <= n && n < 5); */
 
     ASSERT_MAYBE (0 == n);
     ASSERT_MAYBE (1 == n);
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 669c315..d197bda 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -1121,67 +1121,23 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
   update_stmt (last.stmt);
 }
 
-/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument
-   SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to
-   a character array A[N] with unknown length bounded by N, and for
-   strnlen(), by min (N, BOUND).  */
-
-static tree
-maybe_set_strlen_range (tree lhs, tree src, tree bound)
+/* For an LHS that is an SSA_NAME that is the result of a strlen()
+   call, or when BOUND is non-null, of a strnlen() call, set LHS
+   range info to [0, min (MAX, BOUND)] when the range includes more
+   than one value and return LHS.  Otherwise, when the range
+   [MIN, MAX] is such that MIN == MAX, return the tree representation
+   of (MIN). The latter allows callers to fold suitable strnlen() calls
+   to constants.  */
+
+tree
+set_strlen_range (tree lhs, wide_int max, tree bound /* = NULL_TREE */)
 {
   if (TREE_CODE (lhs) != SSA_NAME
       || !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
     return NULL_TREE;
 
-  if (TREE_CODE (src) == SSA_NAME)
-    {
-      gimple *def = SSA_NAME_DEF_STMT (src);
-      if (is_gimple_assign (def)
-	  && gimple_assign_rhs_code (def) == ADDR_EXPR)
-	src = gimple_assign_rhs1 (def);
-    }
-
-  wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
   wide_int min = wi::zero (max.get_precision ());
 
-  if (TREE_CODE (src) == ADDR_EXPR)
-    {
-      /* The last array member of a struct can be bigger than its size
-	 suggests if it's treated as a poor-man's flexible array member.  */
-      src = TREE_OPERAND (src, 0);
-      bool src_is_array = TREE_CODE (TREE_TYPE (src)) == ARRAY_TYPE;
-      if (src_is_array
-	  && TREE_CODE (src) != MEM_REF
-	  && !array_at_struct_end_p (src))
-	{
-	  tree type = TREE_TYPE (src);
-	  if (tree size = TYPE_SIZE_UNIT (type))
-	    if (size && TREE_CODE (size) == INTEGER_CST)
-	      max = wi::to_wide (size);
-
-	  /* For strlen() the upper bound above is equal to
-	     the longest string that can be stored in the array
-	     (i.e., it accounts for the terminating nul.  For
-	     strnlen() bump up the maximum by one since the array
-	     need not be nul-terminated.  */
-	  if (!bound && max != 0)
-	    --max;
-	}
-      else
-	{
-	  if (TREE_CODE (src) == COMPONENT_REF && !src_is_array)
-	    src = TREE_OPERAND (src, 1);
-	  if (DECL_P (src))
-	    {
-	      /* Handle the unlikely case of strlen (&c) where c is some
-		 variable.  */
-	      if (tree size = DECL_SIZE_UNIT (src))
-		if (TREE_CODE (size) == INTEGER_CST)
-		  max = wi::to_wide (size);
-	    }
-	}
-    }
-
   if (bound)
     {
       /* For strnlen, adjust MIN and MAX as necessary.  If the bound
@@ -1205,7 +1161,7 @@ maybe_set_strlen_range (tree lhs, tree src, tree bound)
 	    {
 	      /* For a bound in a known range, adjust the range determined
 		 above as necessary.  For a bound in some anti-range or
-		 in an unknown range, use the range determined above.  */
+		 in an unknown range, use the range determined by callers.  */
 	      if (wi::ltu_p (minbound, min))
 		min = minbound;
 	      if (wi::ltu_p (maxbound, max))
@@ -1221,6 +1177,79 @@ maybe_set_strlen_range (tree lhs, tree src, tree bound)
   return lhs;
 }
 
+/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument
+   SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to
+   a character array A[N] with unknown length bounded by N, and for
+   strnlen(), by min (N, BOUND).  */
+
+static tree
+maybe_set_strlen_range (tree lhs, tree src, tree bound)
+{
+  if (TREE_CODE (lhs) != SSA_NAME
+      || !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+    return NULL_TREE;
+
+  if (TREE_CODE (src) == SSA_NAME)
+    {
+      gimple *def = SSA_NAME_DEF_STMT (src);
+      if (is_gimple_assign (def)
+	  && gimple_assign_rhs_code (def) == ADDR_EXPR)
+	src = gimple_assign_rhs1 (def);
+    }
+
+  /* The longest string is PTRDIFF_MAX - 1 bytes including the final
+     NUL so that the difference between a pointer to just past it and
+     one to its beginning is positive.  */
+  wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2;
+
+  if (TREE_CODE (src) == ADDR_EXPR)
+    {
+      /* The last array member of a struct can be bigger than its size
+	 suggests if it's treated as a poor-man's flexible array member.  */
+      src = TREE_OPERAND (src, 0);
+      if (TREE_CODE (src) != MEM_REF
+	  && !array_at_struct_end_p (src))
+	{
+	  tree type = TREE_TYPE (src);
+	  tree size = TYPE_SIZE_UNIT (type);
+	  if (size
+	      && TREE_CODE (size) == INTEGER_CST
+	      && !integer_zerop (size))
+	    {
+	      /* Even though such uses of strlen would be undefined,
+		 avoid relying on arrays of arrays in case some genius
+		 decides to call strlen on an unterminated array element
+		 that's followed by a terminated one.  Likewise, avoid
+		 assuming that a struct array member is necessarily
+		 nul-terminated (the nul may be in the member that
+		 follows).  In those cases, assume that the length
+		 of the string stored in such an array is bounded
+		 by the size of the enclosing object if one can be
+		 determined.  */
+	      tree base = get_base_address (src);
+	      if (VAR_P (base))
+		{
+		  if (tree size = DECL_SIZE_UNIT (base))
+		    if (size
+			&& TREE_CODE (size) == INTEGER_CST
+			&& TREE_CODE (TREE_TYPE (base)) != POINTER_TYPE)
+		      max = wi::to_wide (size);
+		}
+	    }
+
+	  /* For strlen() the upper bound above is equal to
+	     the longest string that can be stored in the array
+	     (i.e., it accounts for the terminating nul.  For
+	     strnlen() bump up the maximum by one since the array
+	     need not be nul-terminated.  */
+	  if (!bound && max != 0)
+	    --max;
+	}
+    }
+
+  return set_strlen_range (lhs, max, bound);
+}
+
 /* Handle a strlen call.  If strlen of the argument is known, replace
    the strlen call with the known value, otherwise remember that strlen
    of the argument is stored in the lhs SSA_NAME.  */
@@ -1989,15 +2018,18 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
     lenrange[0] = lenrange[1] = wi::shwi (~sidx, prec);
   else
     {
-      tree range[2];
-      get_range_strlen (src, range);
-      if (range[0] != NULL_TREE
-	  && TREE_CODE (range[0]) == INTEGER_CST
-	  && range[1] != NULL_TREE
-	  && TREE_CODE (range[1]) == INTEGER_CST)
+      c_strlen_data lendata = { };
+      get_range_strlen (src, &lendata, /* eltsize = */1);
+      if (TREE_CODE (lendata.minlen) == INTEGER_CST
+	  && TREE_CODE (lendata.maxbound) == INTEGER_CST)
 	{
-	  lenrange[0] = wi::to_wide (range[0], prec);
-	  lenrange[1] = wi::to_wide (range[1], prec);
+	  /* When LENDATA.MAXLEN is unknown, reset LENDATA.MINLEN
+	     which stores the length of the shortest known string.  */
+	  if (integer_all_onesp (lendata.maxlen))
+	    lenrange[0] = wi::shwi (0, prec);
+	  else
+	    lenrange[0] = wi::to_wide (lendata.minlen, prec);
+	  lenrange[1] = wi::to_wide (lendata.maxbound, prec);
 	}
       else
 	{
@@ -2114,6 +2146,13 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
       if (wi::to_wide (dstsize) != cntrange[1])
 	return false;
 
+      /* Avoid warning for strncpy(a, b, N) calls where the following
+	 equalities hold:
+	   N == sizeof a && N == sizeof b */
+      if (tree srcsize = compute_objsize (src, 1))
+	if (wi::to_wide (srcsize) == cntrange[1])
+	  return false;
+
       if (cntrange[0] == cntrange[1])
 	return warning_at (callloc, OPT_Wstringop_truncation,
 			   "%G%qD specified bound %E equals destination size",
diff --git a/gcc/tree-ssa-strlen.h b/gcc/tree-ssa-strlen.h
index f427fb7..d92bc24 100644
--- a/gcc/tree-ssa-strlen.h
+++ b/gcc/tree-ssa-strlen.h
@@ -23,5 +23,6 @@
 
 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);
 
 #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]