diff mbox series

[2/4] - relax strlen range optimization to avoid making assumptions about types

Message ID 161061f0-c481-9213-bc6c-25f2669a8ba4@gmail.com
State New
Headers show
Series avoid relying on type information in get_range_strlen | expand

Commit Message

Martin Sebor Oct. 2, 2018, 4:37 p.m. UTC
[2/4] - Relax strlen range optimization to avoid making assumptions
         about types

This main part of this patch is to relax the strlen range
optimization to avoid relying on array types.  Instead, the function
either removes the upper bound of the strlen result altogether, or
constrains it by the size of referenced declaration (without
attempting to deal with common symbols).

A seemingly "big" change here is splitting up the static
get_range_strlen workhorse into two functions to make it easier
to follow.  The attached cc-99999-2-gimple-fold.c.diff-b shows
the diff for the file without considering whitespace changes.

An important change to the get_range_strlen function worth calling
out to is the introduction of the tight_bound local variable.
It controls whether the upper bound computed by the function is
suitable for optimization (the larger value) or warnings
(the smaller value).

Another important change here is replacing the type and fuzzy
arguments to get_range_strlen with a single enum.  The two arguments
were confusing and with all combinations of their possible values
being meaningful.  The original extern get_range_strlen overload
with the type and fuzzy arguments is retained in this patch to
keep the changes contained.  It is removed in [4/4].

Finally, a large number of tests needed adjusting here.  I also
added a few new tests to better exercise the changes.
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 8f71e9c..eb77065 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -66,6 +66,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-vector-builder.h"
 #include "tree-ssa-strlen.h"
 
+static bool
+get_range_strlen (tree, bitmap *, StrlenType, strlen_data_t *);
+
 /* 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
@@ -1261,40 +1264,23 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
   return true;
 }
 
-
-/* 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, 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.
-   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 PDATA->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.
-   Set PDATA->NONSTR if ARG refers to an unterminated constant array.
-   On input, set PDATA->ELTSIZE to 1 for normal single byte character
-   strings, and 2 or 4 for wide characer strings.  */
+/* Helper of get_range_strlen for ARG that is not SSA_NAME.  */
 
 static bool
-get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
+get_range_strlen_tree (tree arg, bitmap *visited, StrlenType type,
 		       strlen_data_t *pdata)
 {
-  tree var, val = NULL_TREE;
-  gimple *def_stmt;
+  gcc_assert (TREE_CODE (arg) != SSA_NAME);
+
+  /* The length computed by this invocation of the function.  */
+  tree val = NULL_TREE;
+
+  /* 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->MAXSIZE.  */
+  bool tight_bound = false;
 
-  if (TREE_CODE (arg) != SSA_NAME)
-    {
   /* 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)
@@ -1306,9 +1292,10 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
 	  if (TREE_CODE (aop0) == INDIRECT_REF
 	      && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
 	    return get_range_strlen (TREE_OPERAND (aop0, 0), visited, type,
-					 fuzzy, pdata);
+				     pdata);
 	}
-	  else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF && fuzzy)
+      else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF
+	       && type == StringSizeRange)
 	{
 	  /* Fail if an array is the last member of a struct object
 	     since it could be treated as a (fake) flexible array
@@ -1326,8 +1313,9 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
 	}
     }
 
-      if (type == 2)
+  if (type == IntegerValue)
     {
+      /* We are computing the maximum value (not string length).  */
       val = arg;
       if (TREE_CODE (val) != INTEGER_CST
 	  || tree_int_cst_sgn (val) < 0)
@@ -1336,41 +1324,42 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
   else
     val = c_strlen (arg, 1, &pdata->nonstr, pdata->eltsize);
 
-      if (!val && fuzzy)
+  if (!val && type == StringSizeRange)
     {
       if (TREE_CODE (arg) == ADDR_EXPR)
-	    return get_range_strlen (TREE_OPERAND (arg, 0), visited, type,
-				     fuzzy, pdata);
+	return get_range_strlen (TREE_OPERAND (arg, 0), visited, type, pdata);
 
       if (TREE_CODE (arg) == ARRAY_REF)
 	{
-	      tree type = TREE_TYPE (TREE_OPERAND (arg, 0));
+	  tree optype = TREE_TYPE (TREE_OPERAND (arg, 0));
 
 	  /* Determine the "innermost" array type.  */
-	      while (TREE_CODE (type) == ARRAY_TYPE
-		     && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
-		type = TREE_TYPE (type);
+	  while (TREE_CODE (optype) == ARRAY_TYPE
+		 && TREE_CODE (TREE_TYPE (optype)) == ARRAY_TYPE)
+	    optype = TREE_TYPE (optype);
 
 	  /* Avoid arrays of pointers.  */
-	      tree eltype = TREE_TYPE (type);
-	      if (TREE_CODE (type) != ARRAY_TYPE
+	  tree eltype = TREE_TYPE (optype);
+	  if (TREE_CODE (optype) != ARRAY_TYPE
 	      || !INTEGRAL_TYPE_P (eltype))
 	    return false;
 
-	      val = TYPE_SIZE_UNIT (type);
+	  /* 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);
+	  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);
 
 	  if (TREE_CODE (TREE_OPERAND (arg, 0)) == COMPONENT_REF
-		  && type == TREE_TYPE (TREE_OPERAND (arg, 0))
+	      && optype == TREE_TYPE (TREE_OPERAND (arg, 0))
 	      && array_at_struct_end_p (TREE_OPERAND (arg, 0)))
 	    pdata->flexarray = true;
+
+	  tight_bound = true;
 	}
       else if (TREE_CODE (arg) == COMPONENT_REF
 	       && (TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1)))
@@ -1381,40 +1370,48 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
 	     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.
+	     the array was 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))
 	    pdata->flexarray = true;
 
-	      arg = TREE_OPERAND (arg, 1);
+	  tree fld = TREE_OPERAND (arg, 1);
 
-	      tree type = TREE_TYPE (arg);
+	  tree optype = TREE_TYPE (fld);
 
-	      while (TREE_CODE (type) == ARRAY_TYPE
-		     && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
-		type = TREE_TYPE (type);
+	  /* 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 (type);
+	  val = TYPE_SIZE_UNIT (optype);
 	  if (!val || integer_zerop (val))
 	    return false;
-	      val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
-				 integer_one_node);
+
+	  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);
-	    }
 
-	  if (VAR_P (arg))
+	  /* 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))
 	{
-	      tree type = TREE_TYPE (arg);
-	      if (POINTER_TYPE_P (type))
-		type = TREE_TYPE (type);
-
-	      if (TREE_CODE (type) == ARRAY_TYPE)
+	  /* 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)
 	    {
-		  val = TYPE_SIZE_UNIT (type);
+	      val = TYPE_SIZE_UNIT (argtype);
 	      if (!val
 		  || TREE_CODE (val) != INTEGER_CST
 		  || integer_zerop (val))
@@ -1433,7 +1430,7 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
 
   /* Adjust the lower bound on the string length as necessary.  */
   if (!pdata->minlen
-	  || (type > 0
+      || (type != StringLength
 	  && TREE_CODE (pdata->minlen) == INTEGER_CST
 	  && TREE_CODE (val) == INTEGER_CST
 	  && tree_int_cst_lt (val, pdata->minlen)))
@@ -1441,13 +1438,16 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
 
   if (pdata->maxsize)
     {
-	  /* Adjust the tighter (more optimistic) bound if necessary
-	     and proceed to adjust the more conservative bound.  */
+      /* 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 (TREE_CODE (pdata->maxsize) == INTEGER_CST
-		  && tree_int_cst_lt (pdata->maxsize, val))
+	  if (TREE_CODE (pdata->maxsize) == INTEGER_CST)
+	    {
+	      if (tree_int_cst_lt (pdata->maxsize, val))
 		pdata->maxsize = val;
+	    }
 	  else
 	    pdata->maxsize = build_all_ones_cst (size_type_node);
 	}
@@ -1457,11 +1457,48 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
   else
     pdata->maxsize = val;
 
+  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 (type == StringSizeRange)
+	{
+	  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
+	return false;
+    }
+
   if (pdata->maxlen)
     {
       /* Adjust the more conservative bound if possible/necessary
 	 and fail otherwise.  */
-	  if (type > 0)
+      if (type != StringLength)
 	{
 	  if (TREE_CODE (pdata->maxlen) != INTEGER_CST
 	      || TREE_CODE (val) != INTEGER_CST)
@@ -1472,12 +1509,44 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
 	  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;
-      return true;
-    }
+
+  /* When not computing a size-based range, fail if the conservative
+     bound could  not be determined.  */
+  return type == StringSizeRange || !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 TYPE == IntegerValue), try to determine
+   the maximum constant value.
+   If ARG is an SSA_NAME, follow its use-def chains.  When TYPE ==
+   StringLength, then if PDATA->MAXLEN is not equal to the determined
+   length or if we are unable to determine the length, return false.
+   TYPE determines the kind of value/range to obtain (see StrlenType).
+   VISITED is a bitmap of visited variables.
+   Set PDATA->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.
+   Set PDATA->NONSTR if ARG refers to an unterminated constant array.
+   On input, set PDATA->ELTSIZE to 1 for normal single byte character
+   strings, and 2 or 4 for wide characer strings.  */
+
+static bool
+get_range_strlen (tree arg, bitmap *visited, StrlenType type,
+		  strlen_data_t *pdata)
+{
+  if (TREE_CODE (arg) != SSA_NAME)
+    return get_range_strlen_tree (arg, visited, type, pdata);
 
   /* If ARG is registered for SSA update we cannot look at its defining
      statement.  */
@@ -1490,20 +1559,17 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
   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, visited, type, fuzzy, pdata);
+	    return get_range_strlen (rhs, visited, type, pdata);
           }
 	else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR)
 	  {
@@ -1511,20 +1577,25 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
 			    gimple_assign_rhs3 (def_stmt) };
 
 	    for (unsigned int i = 0; i < 2; i++)
-	      if (!get_range_strlen (ops[i], visited, type, fuzzy, pdata))
+	      if (!get_range_strlen (ops[i], visited, type,  pdata))
 		{
-		  if (fuzzy != 2)
+		  if (type != StringSizeRange)
 		    return false;
-		  pdata->maxsize = build_all_ones_cst (size_type_node);
-		  pdata->maxlen = pdata->maxsize;
+		  /* Set the length range to the maximum to prevent
+		     it from being adjusted in the next iteration but
+		     leave the more conservative MAXSIZE determined
+		     so far alone (or leave it null if it hasn't been
+		     set yet).  */
+		  pdata->minlen = size_zero_node;
+		  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 TYPE == StringSizeRange, 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;
@@ -1538,12 +1609,17 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
             if (arg == gimple_phi_result (def_stmt))
               continue;
 
-	    if (!get_range_strlen (arg, visited, type, fuzzy, pdata))
+	    if (!get_range_strlen (arg, visited, type, pdata))
 	      {
-		if (fuzzy != 2)
+		if (type != StringSizeRange)
 		  return false;
-		pdata->maxsize = build_all_ones_cst (size_type_node);
-		pdata->maxlen = pdata->maxsize;
+		  /* Set the length range to the maximum to prevent
+		     it from being adjusted in subsequent iterations
+		     but leave the more conservative MAXSIZE determined
+		     so far alone (or leave it null if it hasn't been
+		     set yet).  */
+		pdata->minlen = size_zero_node;
+		pdata->maxlen = build_all_ones_cst (size_type_node);
 	      }
           }
         return true;
@@ -1555,30 +1631,28 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
 
 /* 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.
-   If STRICT is true, handle PHIs and COND_EXPRs conservatively; when
-   it's false to handle PHIs and COND_EXPRs optimistically by storing
-   the minimum and maximum of only those strings whose length can be
-   determined, and ignoring others.
-   STRICT of false should be only used for warning code.  */
+   of lengths cannot be determined, and store all in *PDATA.  */
 
-bool
-get_range_strlen (tree arg, bool strict, strlen_data_t *pdata)
+void
+get_range_strlen (tree arg, strlen_data_t *pdata)
 {
   bitmap visited = NULL;
 
-  if (!get_range_strlen (arg, &visited, /* type = */ 1,
-			 /* fuzzy = */ strict ? 1 : 2, pdata))
+  if (!get_range_strlen (arg, &visited, StringSizeRange, pdata))
     {
-      pdata->minlen = NULL_TREE;
-      pdata->maxsize = NULL_TREE;
-      pdata->maxlen = 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 = size_zero_node;
+      pdata->maxlen = build_all_ones_cst (size_type_node);
     }
 
+  /* Unless it's null, leave the more conservative MAXSIZE unchanged.  */
+  if (!pdata->maxsize)
+    pdata->maxsize = pdata->maxlen;
+
   if (visited)
     BITMAP_FREE (visited);
-
-  return pdata->flexarray;
 }
 
 /* Determine the minimum and maximum value or string length that ARG
@@ -1608,37 +1682,56 @@ get_range_strlen (tree arg, bool strict, strlen_data_t *pdata)
 
 bool
 get_range_strlen (tree arg, tree minmaxlen[2], unsigned eltsize,
-		  bool strict, tree *nonstr /* = NULL */)
+		  bool strict /* = true */, tree *nonstr /* = NULL */)
 {
   strlen_data_t data (eltsize);
 
-  bool ret = get_range_strlen (arg, strict, &data);
+  get_range_strlen (arg, &data);
+  if (strict
+      && integer_zerop (data.minlen)
+      && integer_all_onesp (data.maxlen))
+    minmaxlen[0] = minmaxlen[1] = NULL_TREE;
+  else
+    {
       minmaxlen[0] = data.minlen;
       minmaxlen[1] = data.maxlen;
+    }
 
   if (nonstr)
     *nonstr = data.nonstr;
 
-  return ret;
+  return data.flexarray;
 }
 
-/* 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 TYPE (see StrlenType).
+   For ARG of pointer types, NONSTR indicates if the caller is
+   prepared to handle unterminated strings.   For integer ARG
+   and when TYPE == IntegerValue, NONSTR must be null.
 
    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, StrlenType type, tree *nonstr = NULL)
 {
+  /* A non-null NONSTR is meaningless when determining the maximum
+     value of an integer ARG.  */
+  gcc_assert (type != IntegerValue || nonstr == NULL);
+  /* ARG must have an integral type when TYPE says so.  */
+  gcc_assert (type != IntegerValue || INTEGRAL_TYPE_P (TREE_TYPE (arg)));
+
   bitmap visited = NULL;
 
   strlen_data_t data (1);
 
-  if (!get_range_strlen (arg, &visited, type, /* fuzzy = */ 0, &data))
+  /* Redet DATA.MAXLEN if the call fails or when DATA.MAXLEN
+     is unbounded.  */
+  if (!get_range_strlen (arg, &visited, type, &data))
     data.maxlen = NULL_TREE;
+  else if (data.maxlen && integer_all_onesp (data.maxlen))
+    data.maxlen = NULL_TREE;
+
   if (visited)
     BITMAP_FREE (visited);
 
@@ -1697,7 +1790,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, StringLength, &nonstr);
 
   if (nonstr)
     {
@@ -1743,7 +1836,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, StringLength);
 	  if (slen && !integer_zerop (slen))
 	    warning_at (loc, OPT_Wstringop_truncation,
 			"%G%qD destination unchanged after copying no bytes "
@@ -1765,7 +1858,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, StringLength);
   if (!slen || TREE_CODE (slen) != INTEGER_CST)
     return false;
 
@@ -1987,7 +2080,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, StringLength);
   if (! len)
     return false;
 
@@ -2503,7 +2596,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, StringLength);
   if (!len
       || TREE_CODE (len) != INTEGER_CST)
     return false;
@@ -2591,7 +2684,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, IntegerValue);
   if (! integer_all_onesp (size))
     {
       if (! tree_fits_uhwi_p (len))
@@ -2692,7 +2785,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, StringLengthMax);
   if (! integer_all_onesp (size))
     {
       len = c_strlen (src, 1);
@@ -2787,7 +2880,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, IntegerValue);
   if (! integer_all_onesp (size))
     {
       if (! tree_fits_uhwi_p (len))
@@ -2924,7 +3017,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, IntegerValue);
       if (! tree_fits_uhwi_p (len))
 	{
 	  /* If LEN is not constant, try MAXLEN too.
@@ -3166,7 +3259,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, StringLength);
 	  if (!orig_len)
 	    return false;
 	}
@@ -3299,7 +3392,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, StringLength);
       if (!orig_len || TREE_CODE (orig_len) != INTEGER_CST)
 	return false;
 
@@ -3607,33 +3700,20 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
   gimple *stmt = gsi_stmt (*gsi);
   tree arg = gimple_call_arg (stmt, 0);
 
-  wide_int minlen;
-  wide_int maxlen;
-
-  /* Set to non-null if ARG refers to an unterminated array.  */
+  /* NONSTR is set to non-null if ARG refers to an unterminated array.
+     Bail if the argument is unterminated, is a flexible array member
+     or if the length or its range cannot be determined.  */
   tree nonstr;
   tree lenrange[2];
-  if (!get_range_strlen (arg, lenrange, 1, true, &nonstr)
-      && lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST
-      && lenrange[1] && TREE_CODE (lenrange[1]) == 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]);
-    }
-  else
-    {
-      unsigned prec = TYPE_PRECISION (sizetype);
-
-      minlen = wi::shwi (0, prec);
-      maxlen = wi::to_wide (max_object_size (), prec) - 2;
-    }
+  if (get_range_strlen (arg, lenrange, /* eltsize = */ 1,
+			/* strict = */ true, &nonstr)
+      || nonstr
+      || !lenrange[0] || TREE_CODE (lenrange[0]) != INTEGER_CST
+      || !lenrange[1] || TREE_CODE (lenrange[1]) != INTEGER_CST
+      || integer_all_onesp (lenrange[1]))
+    return false;
 
-  if (minlen == maxlen)
+  if (tree_int_cst_equal (lenrange[0], lenrange[1]))
     {
       lenrange[0] = force_gimple_operand_gsi (gsi, lenrange[0], true, NULL,
 					      true, GSI_SAME_STMT);
@@ -3641,10 +3721,9 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
       return true;
     }
 
+  /* Set the strlen() range to [0, LENRANGE[1]]*/
   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, wi::to_wide (lenrange[1]));
 
   return false;
 }

Comments

Jeff Law Oct. 10, 2018, 9:18 p.m. UTC | #1
On 10/2/18 10:37 AM, Martin Sebor wrote:
> [2/4] - Relax strlen range optimization to avoid making assumptions
>         about types
> 
> This main part of this patch is to relax the strlen range
> optimization to avoid relying on array types.  Instead, the function
> either removes the upper bound of the strlen result altogether, or
> constrains it by the size of referenced declaration (without
> attempting to deal with common symbols).
> 
> A seemingly "big" change here is splitting up the static
> get_range_strlen workhorse into two functions to make it easier
> to follow.  The attached cc-99999-2-gimple-fold.c.diff-b shows
> the diff for the file without considering whitespace changes.
> 
> An important change to the get_range_strlen function worth calling
> out to is the introduction of the tight_bound local variable.
> It controls whether the upper bound computed by the function is
> suitable for optimization (the larger value) or warnings
> (the smaller value).
> 
> Another important change here is replacing the type and fuzzy
> arguments to get_range_strlen with a single enum.  The two arguments
> were confusing and with all combinations of their possible values
> being meaningful.  The original extern get_range_strlen overload
> with the type and fuzzy arguments is retained in this patch to
> keep the changes contained.  It is removed in [4/4].
> 
> Finally, a large number of tests needed adjusting here.  I also
> added a few new tests to better exercise the changes.
> 
> 
> gcc-99999-2.diff
> 
> [2/4] - Relax strlen range optimization to avoid making assumptions about types
> 
> gcc/ChangeLog:
> 
> 	* calls.c (maybe_warn_nonstring_arg): Test for -1.
> 	* gimple-fold.c (get_range_strlen): Forward declare static.
> 	(get_range_strlen_tree): New helper.
> 	(get_range_strlen): Move tree code into get_range_strlen_tree.
> 	Replace type and fuzzy arguments with a single enum.
> 	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): Declared 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 (StrlenType): New enum.
> 	(get_maxval_strlen): Remove.
> 	(get_range_strlen): Change default argument value to true (strict).
> 	* tree-ssa-strlen.c (set_strlen_range): New function.
> 	(maybe_set_strlen_range): Call it.  Make static.
> 	(maybe_diag_stxncpy_trunc): Use 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.
So from a review standpoint, a distinct patch that splits up the
function, but makes no behavioral changes would help.  That can be gated
in quickly and it reduces the amount of noise one has to sort through in
the changes that affect behavior.  Though the git diff ignoring
whitespace definitely helped.

Camelcase is verboten.  Please don't use it (StrlenType and its
associated enumeration values).   Make the enumeration type lower case
and make the enumeration values all caps.  That's standard practice.

Was this tested independently?  I applied patch #1 and this to my tree,
and I get a bunch of regressions.

builtin-snprintf-warn-?.c
builtin-sprintf-warn-?.c
pr79376.c

In all there's ~75 regressions with this patch.

It's unclear if those are inherent in the patch, a result of previous
API changes (c_strlen in particular), or me incorrectly resolving
conflicts for various hunks.  Some may be fixed in #3 or #4.  In which
case we just need to be aware of the dependencies between the patches
from a testsuite regression standpoint.

It would be advisable to go back to Bernd's patch and see if his new
tests (strlenopt-59 and strlenopt-60) are covered by the new tests
you've included here.

Y'all approached changing the existing tests in significantly different
ways.  It's a bit hard to tease out if there's any other testsuite
changes from Bernd that we'd want to pick up.  If you could take a look
at the other changes Bernd made to the testsuite and see if you're
covering everything he did, it'd be helpful.

Jeff
Martin Sebor Oct. 11, 2018, 3:18 a.m. UTC | #2
On 10/10/2018 03:18 PM, Jeff Law wrote:
> On 10/2/18 10:37 AM, Martin Sebor wrote:
>> [2/4] - Relax strlen range optimization to avoid making assumptions
>>         about types
>>
>> This main part of this patch is to relax the strlen range
>> optimization to avoid relying on array types.  Instead, the function
>> either removes the upper bound of the strlen result altogether, or
>> constrains it by the size of referenced declaration (without
>> attempting to deal with common symbols).
>>
>> A seemingly "big" change here is splitting up the static
>> get_range_strlen workhorse into two functions to make it easier
>> to follow.  The attached cc-99999-2-gimple-fold.c.diff-b shows
>> the diff for the file without considering whitespace changes.
>>
>> An important change to the get_range_strlen function worth calling
>> out to is the introduction of the tight_bound local variable.
>> It controls whether the upper bound computed by the function is
>> suitable for optimization (the larger value) or warnings
>> (the smaller value).
>>
>> Another important change here is replacing the type and fuzzy
>> arguments to get_range_strlen with a single enum.  The two arguments
>> were confusing and with all combinations of their possible values
>> being meaningful.  The original extern get_range_strlen overload
>> with the type and fuzzy arguments is retained in this patch to
>> keep the changes contained.  It is removed in [4/4].
>>
>> Finally, a large number of tests needed adjusting here.  I also
>> added a few new tests to better exercise the changes.
>>
>>
>> gcc-99999-2.diff
>>
>> [2/4] - Relax strlen range optimization to avoid making assumptions about types
>>
>> gcc/ChangeLog:
>>
>> 	* calls.c (maybe_warn_nonstring_arg): Test for -1.
>> 	* gimple-fold.c (get_range_strlen): Forward declare static.
>> 	(get_range_strlen_tree): New helper.
>> 	(get_range_strlen): Move tree code into get_range_strlen_tree.
>> 	Replace type and fuzzy arguments with a single enum.
>> 	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): Declared 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 (StrlenType): New enum.
>> 	(get_maxval_strlen): Remove.
>> 	(get_range_strlen): Change default argument value to true (strict).
>> 	* tree-ssa-strlen.c (set_strlen_range): New function.
>> 	(maybe_set_strlen_range): Call it.  Make static.
>> 	(maybe_diag_stxncpy_trunc): Use 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.
> So from a review standpoint, a distinct patch that splits up the
> function, but makes no behavioral changes would help.  That can be gated
> in quickly and it reduces the amount of noise one has to sort through in
> the changes that affect behavior.  Though the git diff ignoring
> whitespace definitely helped.

The part with no behavioral changes is patch 1/4.  This part is
meant to relax the optimization.  The other changes like adding
the enum are meant to help make the code readable.

>
> Camelcase is verboten.  Please don't use it (StrlenType and its
> associated enumeration values).   Make the enumeration type lower case
> and make the enumeration values all caps.  That's standard practice.
>
> Was this tested independently?  I applied patch #1 and this to my tree,
> and I get a bunch of regressions.
>
> builtin-snprintf-warn-?.c
> builtin-sprintf-warn-?.c
> pr79376.c
>
> In all there's ~75 regressions with this patch.

I tested each patch individually but overlooked some regressions
among all the intermittent failures.  Ultimately, it only makes
sense to me to either apply the whole series or nothing.  I don't
think there are "nuggets" in there that could be applied
independently of the rest.  At least it wasn't my expectation
-- I only broke it up to make the review easier.

>
> It's unclear if those are inherent in the patch, a result of previous
> API changes (c_strlen in particular), or me incorrectly resolving
> conflicts for various hunks.  Some may be fixed in #3 or #4.  In which
> case we just need to be aware of the dependencies between the patches
> from a testsuite regression standpoint.
>
> It would be advisable to go back to Bernd's patch and see if his new
> tests (strlenopt-59 and strlenopt-60) are covered by the new tests
> you've included here.
>
> Y'all approached changing the existing tests in significantly different
> ways.  It's a bit hard to tease out if there's any other testsuite
> changes from Bernd that we'd want to pick up.  If you could take a look
> at the other changes Bernd made to the testsuite and see if you're
> covering everything he did, it'd be helpful.

I kept individual assertions as long as they're verifying
a subrange of the max length (i.e., < PTRDIFF_MAX).   I got
rid of those where the range became essentially open ended,
and added more where I thought coverage could be improved.
My impression of Bernd's test suite changes is that he either
chucked the lot that failed or xfailed them wholesale.

I'm traveling next week so I won't have time to do much until
I get back.

Martin
diff mbox series

Patch

[2/4] - Relax strlen range optimization to avoid making assumptions about types

gcc/ChangeLog:

	* calls.c (maybe_warn_nonstring_arg): Test for -1.
	* gimple-fold.c (get_range_strlen): Forward declare static.
	(get_range_strlen_tree): New helper.
	(get_range_strlen): Move tree code into get_range_strlen_tree.
	Replace type and fuzzy arguments with a single enum.
	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): Declared 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 (StrlenType): New enum.
	(get_maxval_strlen): Remove.
	(get_range_strlen): Change default argument value to true (strict).
	* tree-ssa-strlen.c (set_strlen_range): New function.
	(maybe_set_strlen_range): Call it.  Make static.
	(maybe_diag_stxncpy_trunc): Use 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.

diff --git a/gcc/calls.c b/gcc/calls.c
index 11f00ad..617757e6 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1650,7 +1650,7 @@  maybe_warn_nonstring_arg (tree fndecl, tree exp)
 	}
     }
 
-  if (lenrng[1] && TREE_CODE (lenrng[1]) == INTEGER_CST)
+  if (lenrng[1] && !integer_all_onesp (lenrng[1]))
     {
       /* Add one for the nul.  */
       lenrng[1] = const_binop (PLUS_EXPR, TREE_TYPE (lenrng[1]),
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 8f71e9c..eb77065 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -66,6 +66,9 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-vector-builder.h"
 #include "tree-ssa-strlen.h"
 
+static bool
+get_range_strlen (tree, bitmap *, StrlenType, strlen_data_t *);
+
 /* 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
@@ -1261,224 +1264,290 @@  gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
   return true;
 }
 
-
-/* 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, 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.
-   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 PDATA->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.
-   Set PDATA->NONSTR if ARG refers to an unterminated constant array.
-   On input, set PDATA->ELTSIZE to 1 for normal single byte character
-   strings, and 2 or 4 for wide characer strings.  */
+/* Helper of get_range_strlen for ARG that is not SSA_NAME.  */
 
 static bool
-get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
-		  strlen_data_t *pdata)
+get_range_strlen_tree (tree arg, bitmap *visited, StrlenType type,
+		       strlen_data_t *pdata)
 {
-  tree var, val = NULL_TREE;
-  gimple *def_stmt;
+  gcc_assert (TREE_CODE (arg) != SSA_NAME);
 
-  if (TREE_CODE (arg) != SSA_NAME)
+  /* The length computed by this invocation of the function.  */
+  tree val = NULL_TREE;
+
+  /* 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->MAXSIZE.  */
+  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), visited, type,
-					 fuzzy, pdata);
-	    }
-	  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, type,
+				     pdata);
 	}
-
-      if (type == 2)
+      else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF
+	       && type == StringSizeRange)
 	{
-	  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
-	val = c_strlen (arg, 1, &pdata->nonstr, pdata->eltsize);
+    }
+
+  if (type == IntegerValue)
+    {
+      /* 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
+    val = c_strlen (arg, 1, &pdata->nonstr, pdata->eltsize);
 
-      if (!val && fuzzy)
+  if (!val && type == StringSizeRange)
+    {
+      if (TREE_CODE (arg) == ADDR_EXPR)
+	return get_range_strlen (TREE_OPERAND (arg, 0), visited, type, pdata);
+
+      if (TREE_CODE (arg) == ARRAY_REF)
 	{
-	  if (TREE_CODE (arg) == ADDR_EXPR)
-	    return get_range_strlen (TREE_OPERAND (arg, 0), visited, type,
-				     fuzzy, pdata);
+	  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);
 
-	      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);
+	  if (TREE_CODE (TREE_OPERAND (arg, 0)) == COMPONENT_REF
+	      && optype == TREE_TYPE (TREE_OPERAND (arg, 0))
+	      && array_at_struct_end_p (TREE_OPERAND (arg, 0)))
+	    pdata->flexarray = true;
 
-	      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)))
-		pdata->flexarray = true;
-	    }
-	  else if (TREE_CODE (arg) == COMPONENT_REF
-		   && (TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1)))
-		       == ARRAY_TYPE))
+	  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.
+	     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))
+	    pdata->flexarray = true;
+
+	  tree fld = TREE_OPERAND (arg, 1);
+
+	  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))
-		pdata->flexarray = 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.  */
 	      pdata->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.  */
-		  pdata->minlen = ssize_int (0);
-		}
-	    }
 	}
+    }
 
-      if (!val)
-	return false;
+  if (!val)
+    return false;
 
-      /* Adjust the lower bound on the string length as necessary.  */
-      if (!pdata->minlen
-	  || (type > 0
-	      && TREE_CODE (pdata->minlen) == INTEGER_CST
-	      && TREE_CODE (val) == INTEGER_CST
-	      && tree_int_cst_lt (val, pdata->minlen)))
-	pdata->minlen = val;
+  /* Adjust the lower bound on the string length as necessary.  */
+  if (!pdata->minlen
+      || (type != StringLength
+	  && TREE_CODE (pdata->minlen) == INTEGER_CST
+	  && TREE_CODE (val) == INTEGER_CST
+	  && tree_int_cst_lt (val, pdata->minlen)))
+    pdata->minlen = val;
 
-      if (pdata->maxsize)
+  if (pdata->maxsize)
+    {
+      /* Adjust the tighter (more optimistic) string length bound
+	 if necessary and proceed to adjust the more conservative
+	 bound.  */
+      if (TREE_CODE (val) == INTEGER_CST)
 	{
-	  /* Adjust the tighter (more optimistic) bound if necessary
-	     and proceed to adjust the more conservative bound.  */
-	  if (TREE_CODE (val) == INTEGER_CST)
+	  if (TREE_CODE (pdata->maxsize) == INTEGER_CST)
 	    {
-	      if (TREE_CODE (pdata->maxsize) == INTEGER_CST
-		  && tree_int_cst_lt (pdata->maxsize, val))
+	      if (tree_int_cst_lt (pdata->maxsize, val))
 		pdata->maxsize = val;
-	      else
-		pdata->maxsize = build_all_ones_cst (size_type_node);
 	    }
 	  else
-	    pdata->maxsize = val;
+	    pdata->maxsize = build_all_ones_cst (size_type_node);
 	}
       else
 	pdata->maxsize = val;
+    }
+  else
+    pdata->maxsize = val;
 
-      if (pdata->maxlen)
+  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 (type == StringSizeRange)
 	{
-	  /* Adjust the more conservative bound if possible/necessary
-	     and fail otherwise.  */
-	  if (type > 0)
+	  poly_int64 offset;
+	  tree base = get_addr_base_and_unit_offset (arg, &offset);
+	  if (!base)
 	    {
-	      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;
+	      /* 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, pdata->maxlen) != 1)
-	    return false;
 	}
+      else
+	return false;
+    }
 
-      pdata->maxlen = val;
-      return true;
+  if (pdata->maxlen)
+    {
+      /* Adjust the more conservative bound if possible/necessary
+	 and fail otherwise.  */
+      if (type != StringLength)
+	{
+	  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 type == StringSizeRange || !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 TYPE == IntegerValue), try to determine
+   the maximum constant value.
+   If ARG is an SSA_NAME, follow its use-def chains.  When TYPE ==
+   StringLength, then if PDATA->MAXLEN is not equal to the determined
+   length or if we are unable to determine the length, return false.
+   TYPE determines the kind of value/range to obtain (see StrlenType).
+   VISITED is a bitmap of visited variables.
+   Set PDATA->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.
+   Set PDATA->NONSTR if ARG refers to an unterminated constant array.
+   On input, set PDATA->ELTSIZE to 1 for normal single byte character
+   strings, and 2 or 4 for wide characer strings.  */
+
+static bool
+get_range_strlen (tree arg, bitmap *visited, StrlenType type,
+		  strlen_data_t *pdata)
+{
+  if (TREE_CODE (arg) != SSA_NAME)
+    return get_range_strlen_tree (arg, visited, type, pdata);
+
   /* If ARG is registered for SSA update we cannot look at its defining
      statement.  */
   if (name_registered_for_update_p (arg))
@@ -1490,20 +1559,17 @@  get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
   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, visited, type, fuzzy, pdata);
+	    return get_range_strlen (rhs, visited, type, pdata);
           }
 	else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR)
 	  {
@@ -1511,20 +1577,25 @@  get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
 			    gimple_assign_rhs3 (def_stmt) };
 
 	    for (unsigned int i = 0; i < 2; i++)
-	      if (!get_range_strlen (ops[i], visited, type, fuzzy, pdata))
+	      if (!get_range_strlen (ops[i], visited, type,  pdata))
 		{
-		  if (fuzzy != 2)
+		  if (type != StringSizeRange)
 		    return false;
-		  pdata->maxsize = build_all_ones_cst (size_type_node);
-		  pdata->maxlen = pdata->maxsize;
+		  /* Set the length range to the maximum to prevent
+		     it from being adjusted in the next iteration but
+		     leave the more conservative MAXSIZE determined
+		     so far alone (or leave it null if it hasn't been
+		     set yet).  */
+		  pdata->minlen = size_zero_node;
+		  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 TYPE == StringSizeRange, 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;
@@ -1538,12 +1609,17 @@  get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
             if (arg == gimple_phi_result (def_stmt))
               continue;
 
-	    if (!get_range_strlen (arg, visited, type, fuzzy, pdata))
+	    if (!get_range_strlen (arg, visited, type, pdata))
 	      {
-		if (fuzzy != 2)
+		if (type != StringSizeRange)
 		  return false;
-		pdata->maxsize = build_all_ones_cst (size_type_node);
-		pdata->maxlen = pdata->maxsize;
+		  /* Set the length range to the maximum to prevent
+		     it from being adjusted in subsequent iterations
+		     but leave the more conservative MAXSIZE determined
+		     so far alone (or leave it null if it hasn't been
+		     set yet).  */
+		pdata->minlen = size_zero_node;
+		pdata->maxlen = build_all_ones_cst (size_type_node);
 	      }
           }
         return true;
@@ -1555,30 +1631,28 @@  get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
 
 /* 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.
-   If STRICT is true, handle PHIs and COND_EXPRs conservatively; when
-   it's false to handle PHIs and COND_EXPRs optimistically by storing
-   the minimum and maximum of only those strings whose length can be
-   determined, and ignoring others.
-   STRICT of false should be only used for warning code.  */
+   of lengths cannot be determined, and store all in *PDATA.  */
 
-bool
-get_range_strlen (tree arg, bool strict, strlen_data_t *pdata)
+void
+get_range_strlen (tree arg, strlen_data_t *pdata)
 {
   bitmap visited = NULL;
 
-  if (!get_range_strlen (arg, &visited, /* type = */ 1,
-			 /* fuzzy = */ strict ? 1 : 2, pdata))
+  if (!get_range_strlen (arg, &visited, StringSizeRange, pdata))
     {
-      pdata->minlen = NULL_TREE;
-      pdata->maxsize = NULL_TREE;
-      pdata->maxlen = 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 = size_zero_node;
+      pdata->maxlen = build_all_ones_cst (size_type_node);
     }
 
+  /* Unless it's null, leave the more conservative MAXSIZE unchanged.  */
+  if (!pdata->maxsize)
+    pdata->maxsize = pdata->maxlen;
+
   if (visited)
     BITMAP_FREE (visited);
-
-  return pdata->flexarray;
 }
 
 /* Determine the minimum and maximum value or string length that ARG
@@ -1608,37 +1682,56 @@  get_range_strlen (tree arg, bool strict, strlen_data_t *pdata)
 
 bool
 get_range_strlen (tree arg, tree minmaxlen[2], unsigned eltsize,
-		  bool strict, tree *nonstr /* = NULL */)
+		  bool strict /* = true */, tree *nonstr /* = NULL */)
 {
   strlen_data_t data (eltsize);
 
-  bool ret = get_range_strlen (arg, strict, &data);
-  minmaxlen[0] = data.minlen;
-  minmaxlen[1] = data.maxlen;
+  get_range_strlen (arg, &data);
+  if (strict
+      && integer_zerop (data.minlen)
+      && integer_all_onesp (data.maxlen))
+    minmaxlen[0] = minmaxlen[1] = NULL_TREE;
+  else
+    {
+      minmaxlen[0] = data.minlen;
+      minmaxlen[1] = data.maxlen;
+    }
 
   if (nonstr)
     *nonstr = data.nonstr;
 
-  return ret;
+  return data.flexarray;
 }
 
-/* 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 TYPE (see StrlenType).
+   For ARG of pointer types, NONSTR indicates if the caller is
+   prepared to handle unterminated strings.   For integer ARG
+   and when TYPE == IntegerValue, NONSTR must be null.
 
    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, StrlenType type, tree *nonstr = NULL)
 {
+  /* A non-null NONSTR is meaningless when determining the maximum
+     value of an integer ARG.  */
+  gcc_assert (type != IntegerValue || nonstr == NULL);
+  /* ARG must have an integral type when TYPE says so.  */
+  gcc_assert (type != IntegerValue || INTEGRAL_TYPE_P (TREE_TYPE (arg)));
+
   bitmap visited = NULL;
 
   strlen_data_t data (1);
 
-  if (!get_range_strlen (arg, &visited, type, /* fuzzy = */ 0, &data))
+  /* Redet DATA.MAXLEN if the call fails or when DATA.MAXLEN
+     is unbounded.  */
+  if (!get_range_strlen (arg, &visited, type, &data))
+    data.maxlen = NULL_TREE;
+  else if (data.maxlen && integer_all_onesp (data.maxlen))
     data.maxlen = NULL_TREE;
+
   if (visited)
     BITMAP_FREE (visited);
 
@@ -1697,7 +1790,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, StringLength, &nonstr);
 
   if (nonstr)
     {
@@ -1743,7 +1836,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, StringLength);
 	  if (slen && !integer_zerop (slen))
 	    warning_at (loc, OPT_Wstringop_truncation,
 			"%G%qD destination unchanged after copying no bytes "
@@ -1765,7 +1858,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, StringLength);
   if (!slen || TREE_CODE (slen) != INTEGER_CST)
     return false;
 
@@ -1987,7 +2080,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, StringLength);
   if (! len)
     return false;
 
@@ -2503,7 +2596,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, StringLength);
   if (!len
       || TREE_CODE (len) != INTEGER_CST)
     return false;
@@ -2591,7 +2684,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, IntegerValue);
   if (! integer_all_onesp (size))
     {
       if (! tree_fits_uhwi_p (len))
@@ -2692,7 +2785,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, StringLengthMax);
   if (! integer_all_onesp (size))
     {
       len = c_strlen (src, 1);
@@ -2787,7 +2880,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, IntegerValue);
   if (! integer_all_onesp (size))
     {
       if (! tree_fits_uhwi_p (len))
@@ -2924,7 +3017,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, IntegerValue);
       if (! tree_fits_uhwi_p (len))
 	{
 	  /* If LEN is not constant, try MAXLEN too.
@@ -3166,7 +3259,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, StringLength);
 	  if (!orig_len)
 	    return false;
 	}
@@ -3299,7 +3392,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, StringLength);
       if (!orig_len || TREE_CODE (orig_len) != INTEGER_CST)
 	return false;
 
@@ -3607,33 +3700,20 @@  gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
   gimple *stmt = gsi_stmt (*gsi);
   tree arg = gimple_call_arg (stmt, 0);
 
-  wide_int minlen;
-  wide_int maxlen;
-
-  /* Set to non-null if ARG refers to an unterminated array.  */
+  /* NONSTR is set to non-null if ARG refers to an unterminated array.
+     Bail if the argument is unterminated, is a flexible array member
+     or if the length or its range cannot be determined.  */
   tree nonstr;
   tree lenrange[2];
-  if (!get_range_strlen (arg, lenrange, 1, true, &nonstr)
-      && lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST
-      && lenrange[1] && TREE_CODE (lenrange[1]) == 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]);
-    }
-  else
-    {
-      unsigned prec = TYPE_PRECISION (sizetype);
-
-      minlen = wi::shwi (0, prec);
-      maxlen = wi::to_wide (max_object_size (), prec) - 2;
-    }
+  if (get_range_strlen (arg, lenrange, /* eltsize = */ 1,
+			/* strict = */ true, &nonstr)
+      || nonstr
+      || !lenrange[0] || TREE_CODE (lenrange[0]) != INTEGER_CST
+      || !lenrange[1] || TREE_CODE (lenrange[1]) != INTEGER_CST
+      || integer_all_onesp (lenrange[1]))
+    return false;
 
-  if (minlen == maxlen)
+  if (tree_int_cst_equal (lenrange[0], lenrange[1]))
     {
       lenrange[0] = force_gimple_operand_gsi (gsi, lenrange[0], true, NULL,
 					      true, GSI_SAME_STMT);
@@ -3641,10 +3721,9 @@  gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
       return true;
     }
 
+  /* Set the strlen() range to [0, LENRANGE[1]]*/
   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, wi::to_wide (lenrange[1]));
 
   return false;
 }
diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h
index 0d523e7..b086c12 100644
--- a/gcc/gimple-fold.h
+++ b/gcc/gimple-fold.h
@@ -70,11 +70,25 @@  struct strlen_data_t
       flexarray () { }
 };
 
-extern bool get_range_strlen (tree, strlen_data_t * = NULL);
+extern void get_range_strlen (tree, strlen_data_t *);
+
+enum StrlenType {
+  /* Compute the exact constant string length.  */
+  StringLength,
+  /* Compute the maximum constant string length.  */
+  StringLengthMax,
+  /* 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.  */
+  StringSizeRange,
+  /* Determine the integer value of the argument (not string length).  */
+  IntegerValue
+};
 
-extern bool get_range_strlen (tree, tree[2], unsigned = 1,
-			      bool = false, tree * = NULL);
-extern tree get_maxval_strlen (tree, int, tree * = NULL);
+extern bool get_range_strlen (tree, tree[2], unsigned = 1, bool = true,
+			      tree * = NULL);
 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/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 9ec718f..3d879f1 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-51.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-51.c
@@ -1,6 +1,6 @@ 
 /* 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"
 
@@ -22,20 +22,6 @@ 
 #define ELIM(expr) \
   if (!(expr)) FAIL (in_true_branch_not_eliminated, __COUNTER__); else (void)0
 
-/* Macro to emit a call to a function named
-     call_made_in_{true,false}_branch_on_line_NNN()
-   for each call that's expected to be retained.  The dg-final
-   scan-tree-dump-time directive at the bottom of the test verifies
-   that the expected number of both kinds of calls appears in output
-   (a pair for each line with the invocation of the KEEP() macro.  */
-#define DO_KEEP(expr, counter)			\
-  if (expr)					\
-    FAIL (made_in_true_branch, counter);	\
-  else						\
-    FAIL (made_in_false_branch, counter)
-
-#define KEEP(expr) DO_KEEP (expr, __COUNTER__)
-
 #define T(s, n) ELIM (strlen (s) == n)
 
 
@@ -56,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);
@@ -81,7 +67,7 @@  const char a9_9[][9][9] = {
   { 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)					\
@@ -98,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]) > (0 + 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/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 3fa5ef5..f200f04 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,13 @@  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)
+      strlen_data_t lendata (1);
+      get_range_strlen (src, &lendata);
+      if (TREE_CODE (lendata.minlen) == INTEGER_CST
+	  && TREE_CODE (lendata.maxsize) == INTEGER_CST)
 	{
-	  lenrange[0] = wi::to_wide (range[0], prec);
-	  lenrange[1] = wi::to_wide (range[1], prec);
+	  lenrange[0] = wi::to_wide (lendata.minlen, prec);
+	  lenrange[1] = wi::to_wide (lendata.maxsize, prec);
 	}
       else
 	{
@@ -2114,6 +2141,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