diff mbox series

fold strlen() of aggregate members (PR 77357)

Message ID 64e1b845-0bdb-cc86-302a-a5b1897d7ef1@gmail.com
State New
Headers show
Series fold strlen() of aggregate members (PR 77357) | expand

Commit Message

Martin Sebor July 5, 2018, 11:54 p.m. UTC
GCC folds accesses to members of constant aggregates except
for character arrays/strings.  For example, the strlen() call
below is not folded:

   const char a[][4] = { "1", "12" };

   int f (void) { retturn strlen (a[1]); }

The attached change set enhances the string_constant() function
to make it possible to extract string constants from aggregate
initializers (CONSTRUCTORS).

The initial solution was much simpler but as is often the case,
MEM_REF made it fail to fold things like:

   int f (void) { retturn strlen (a[1] + 1); }

Handling those made the project a bit more interesting and
the final solution somewhat more involved.

To handle offsets into aggregate string members the patch also
extends the fold_ctor_reference() function to extract entire
string array initializers even if the offset points past
the beginning of the string and even though the size and
exact type of the reference are not known (there isn't enough
information in a MEM_REF to determine that).

Tested along with the patch for PR 86415 on x86_64-linux.

Martin

Comments

Richard Biener July 6, 2018, 3:52 p.m. UTC | #1
On Fri, Jul 6, 2018 at 1:54 AM Martin Sebor <msebor@gmail.com> wrote:
>
> GCC folds accesses to members of constant aggregates except
> for character arrays/strings.  For example, the strlen() call
> below is not folded:
>
>    const char a[][4] = { "1", "12" };
>
>    int f (void) { retturn strlen (a[1]); }
>
> The attached change set enhances the string_constant() function
> to make it possible to extract string constants from aggregate
> initializers (CONSTRUCTORS).
>
> The initial solution was much simpler but as is often the case,
> MEM_REF made it fail to fold things like:
>
>    int f (void) { retturn strlen (a[1] + 1); }
>
> Handling those made the project a bit more interesting and
> the final solution somewhat more involved.
>
> To handle offsets into aggregate string members the patch also
> extends the fold_ctor_reference() function to extract entire
> string array initializers even if the offset points past
> the beginning of the string and even though the size and
> exact type of the reference are not known (there isn't enough
> information in a MEM_REF to determine that).
>
> Tested along with the patch for PR 86415 on x86_64-linux.

+      if (TREE_CODE (init) == CONSTRUCTOR)
+       {
+         tree type;
+         if (TREE_CODE (arg) == ARRAY_REF
+             || TREE_CODE (arg) == MEM_REF)
+           type = TREE_TYPE (arg);
+         else if (TREE_CODE (arg) == COMPONENT_REF)
+           {
+             tree field = TREE_OPERAND (arg, 1);
+             type = TREE_TYPE (field);
+           }
+         else
+           return NULL_TREE;

what's wrong with just

    type = TREE_TYPE (field);

?

+         base_off *= BITS_PER_UNIT;

poly_uint64 isn't enough for "bits", with wide-int you'd use offset_int,
for poly you'd then use poly_offset?

You extend fold_ctor_reference to treat size == 0 specially but then
bother to compute a size here - that looks unneeded?

While the offset of the reference determines the first field in the
CONSTRUCTOR, how do you know the access doesn't touch
adjacent ones?  STRING_CSTs do not have to be '\0' terminated,
so consider

  char x[2][4] = { "abcd", "abcd" };

and MEM[&x] with a char[8] type?  memcpy "inlining" will create
such MEMs for example.

@@ -6554,8 +6577,16 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
       tree byte_offset = DECL_FIELD_OFFSET (cfield);
       tree field_offset = DECL_FIELD_BIT_OFFSET (cfield);
       tree field_size = DECL_SIZE (cfield);
-      offset_int bitoffset;
-      offset_int bitoffset_end, access_end;
+
+      if (!field_size && TREE_CODE (cval) == STRING_CST)
+       {
+         /* Determine the size of the flexible array member from
+            the size of the string initializer provided for it.  */
+         unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval);
+         tree eltype = TREE_TYPE (TREE_TYPE (cval));
+         len *= tree_to_uhwi (TYPE_SIZE (eltype));
+         field_size = build_int_cst (size_type_node, len);
+       }

Why does this only apply to STRING_CST initializers and not CONSTRUCTORS,
say, for

struct S { int i; int a[]; } s = { 1, { 2, 3, 4, 5, 6 } };

?  And why not use simply

  field_size = TYPE_SIZE (TREE_TYPE (cval));

like you do in c_strlen?

Otherwise looks reasonable.

Thanks,
Richard.

> Martin
Martin Sebor July 8, 2018, 2:56 a.m. UTC | #2
On 07/06/2018 09:52 AM, Richard Biener wrote:
> On Fri, Jul 6, 2018 at 1:54 AM Martin Sebor <msebor@gmail.com> wrote:
>>
>> GCC folds accesses to members of constant aggregates except
>> for character arrays/strings.  For example, the strlen() call
>> below is not folded:
>>
>>    const char a[][4] = { "1", "12" };
>>
>>    int f (void) { retturn strlen (a[1]); }
>>
>> The attached change set enhances the string_constant() function
>> to make it possible to extract string constants from aggregate
>> initializers (CONSTRUCTORS).
>>
>> The initial solution was much simpler but as is often the case,
>> MEM_REF made it fail to fold things like:
>>
>>    int f (void) { retturn strlen (a[1] + 1); }
>>
>> Handling those made the project a bit more interesting and
>> the final solution somewhat more involved.
>>
>> To handle offsets into aggregate string members the patch also
>> extends the fold_ctor_reference() function to extract entire
>> string array initializers even if the offset points past
>> the beginning of the string and even though the size and
>> exact type of the reference are not known (there isn't enough
>> information in a MEM_REF to determine that).
>>
>> Tested along with the patch for PR 86415 on x86_64-linux.
>
> +      if (TREE_CODE (init) == CONSTRUCTOR)
> +       {
> +         tree type;
> +         if (TREE_CODE (arg) == ARRAY_REF
> +             || TREE_CODE (arg) == MEM_REF)
> +           type = TREE_TYPE (arg);
> +         else if (TREE_CODE (arg) == COMPONENT_REF)
> +           {
> +             tree field = TREE_OPERAND (arg, 1);
> +             type = TREE_TYPE (field);
> +           }
> +         else
> +           return NULL_TREE;
>
> what's wrong with just
>
>     type = TREE_TYPE (field);

In response to your comment below abut size I simplified things
further so determining the type a priori is no longer necessary.

> ?
>
> +         base_off *= BITS_PER_UNIT;
>
> poly_uint64 isn't enough for "bits", with wide-int you'd use offset_int,
> for poly you'd then use poly_offset?

Okay, I tried to avoid the overflow.  (Converting between all
these flavors of wide int types is a monumental PITA.)

>
> You extend fold_ctor_reference to treat size == 0 specially but then
> bother to compute a size here - that looks unneeded?

Yes, well spotted, thanks!  I simplified the code so this isn't
necessary, and neither is the type.

>
> While the offset of the reference determines the first field in the
> CONSTRUCTOR, how do you know the access doesn't touch
> adjacent ones?  STRING_CSTs do not have to be '\0' terminated,
> so consider
>
>   char x[2][4] = { "abcd", "abcd" };
>
> and MEM[&x] with a char[8] type?  memcpy "inlining" will create
> such MEMs for example.

The code is only used to find string constants in initializer
expressions where I don't think the size of the access comes
into play.  If a memcpy() call results in a MEM_REF[char[8],
&x, 8] that's fine.  It's a valid reference and we can still
get the underlying character sequence (which is represented
as two STRING_CSTs with the two string literals).  I might
be missing the point of your question.

>
> @@ -6554,8 +6577,16 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
>        tree byte_offset = DECL_FIELD_OFFSET (cfield);
>        tree field_offset = DECL_FIELD_BIT_OFFSET (cfield);
>        tree field_size = DECL_SIZE (cfield);
> -      offset_int bitoffset;
> -      offset_int bitoffset_end, access_end;
> +
> +      if (!field_size && TREE_CODE (cval) == STRING_CST)
> +       {
> +         /* Determine the size of the flexible array member from
> +            the size of the string initializer provided for it.  */
> +         unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval);
> +         tree eltype = TREE_TYPE (TREE_TYPE (cval));
> +         len *= tree_to_uhwi (TYPE_SIZE (eltype));
> +         field_size = build_int_cst (size_type_node, len);
> +       }
>
> Why does this only apply to STRING_CST initializers and not CONSTRUCTORS,
> say, for
>
> struct S { int i; int a[]; } s = { 1, { 2, 3, 4, 5, 6 } };

I can't think of a use for it.  Do you have something in mind?

>
> ?  And why not use simply
>
>   field_size = TYPE_SIZE (TREE_TYPE (cval));
>
> like you do in c_strlen?

Yes, that's simpler, thanks.

>
> Otherwise looks reasonable.

Attached is an updated patch.  I also enhanced the handling
of non-constant indices.  They were already handled before
to a smaller extent.  (There may be other opportunities
here.)

Martin
PR middle-end/77357 - strlen of constant strings not folded

gcc/ChangeLog:

	* builtins.c (c_strlen): Avoid out-of-bounds warnings when
	accessing implicitly initialized array elements.
	* expr.c (string_constant): Handle string initializers of
	character arrays within aggregates.
	* gimple-fold.c (fold_array_ctor_reference): Add argument.
	Store element offset.  As a special case, handle zero size.
	(fold_nonarray_ctor_reference): Same.
	(fold_ctor_reference): Add argument.  Store subobject offset.
	* gimple-fold.h (fold_ctor_reference): Add argument.

gcc/testsuite/ChangeLog:

	PR middle-end/77357
	* gcc.dg/strlenopt-49.c: New test.
	* gcc.dg/strlenopt-50.c: New test.
	* gcc.dg/strlenopt-51.c: New test.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 91658e8..820d6c2 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -602,8 +602,15 @@ c_strlen (tree src, int only_value)
     = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (src))));
 
   /* Set MAXELTS to sizeof (SRC) / sizeof (*SRC) - 1, the maximum possible
-     length of SRC.  */
-  unsigned maxelts = TREE_STRING_LENGTH (src) / eltsize - 1;
+     length of SRC.  Prefer TYPE_SIZE() to TREE_STRING_LENGTH() if possible
+     in case the latter is less than the size of the array.  */
+  HOST_WIDE_INT maxelts = TREE_STRING_LENGTH (src);
+  tree type = TREE_TYPE (src);
+  if (tree size = TYPE_SIZE_UNIT (type))
+    if (tree_fits_shwi_p (size))
+      maxelts = tree_to_uhwi (size);
+
+  maxelts = maxelts / eltsize - 1;
 
   /* PTR can point to the byte representation of any string type, including
      char* and wchar_t*.  */
@@ -629,7 +636,6 @@ c_strlen (tree src, int only_value)
 	 what he gets.  Subtract the offset from the length of the string,
 	 and return that.  This would perhaps not be valid if we were dealing
 	 with named arrays in addition to literal string constants.  */
-
       return size_diffop_loc (loc, size_int (maxelts * eltsize), byteoff);
     }
 
diff --git a/gcc/expr.c b/gcc/expr.c
index 56751df..fcb5b0b 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -54,11 +54,13 @@ along with GCC; see the file COPYING3.  If not see
 #include "reload.h"
 #include "langhooks.h"
 #include "common/common-target.h"
+#include "tree-dfa.h"
 #include "tree-ssa-live.h"
 #include "tree-outof-ssa.h"
 #include "tree-ssa-address.h"
 #include "builtins.h"
 #include "ccmp.h"
+#include "gimple-fold.h"
 #include "rtx-vector-builder.h"
 
 
@@ -11267,61 +11269,45 @@ is_aligning_offset (const_tree offset, const_tree exp)
 }
 
 /* Return the tree node if an ARG corresponds to a string constant or zero
-   if it doesn't.  If we return nonzero, set *PTR_OFFSET to the offset
-   in bytes within the string that ARG is accessing.  The type of the
-   offset will be `sizetype'.  */
+   if it doesn't.  If we return nonzero, set *PTR_OFFSET to the (possibly
+   non-constant) offset in bytes within the string that ARG is accessing.
+   The type of the offset is sizetype.  */
 
 tree
 string_constant (tree arg, tree *ptr_offset)
 {
-  tree array, offset, lower_bound;
+  tree array;
   STRIP_NOPS (arg);
 
+  /* Non-constant index into the character array in an ARRAY_REF
+     expression or null.  */
+  tree varidx = NULL_TREE;
+
+  poly_int64 base_off = 0;
+
   if (TREE_CODE (arg) == ADDR_EXPR)
     {
-      if (TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST)
-	{
-	  *ptr_offset = size_zero_node;
-	  return TREE_OPERAND (arg, 0);
-	}
-      else if (TREE_CODE (TREE_OPERAND (arg, 0)) == VAR_DECL)
-	{
-	  array = TREE_OPERAND (arg, 0);
-	  offset = size_zero_node;
-	}
-      else if (TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
+      arg = TREE_OPERAND (arg, 0);
+      tree ref = arg;
+      if (TREE_CODE (arg) == ARRAY_REF)
 	{
-	  array = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
-	  offset = TREE_OPERAND (TREE_OPERAND (arg, 0), 1);
-	  if (TREE_CODE (array) != STRING_CST && !VAR_P (array))
-	    return 0;
-
-	  /* Check if the array has a nonzero lower bound.  */
-	  lower_bound = array_ref_low_bound (TREE_OPERAND (arg, 0));
-	  if (!integer_zerop (lower_bound))
+	  tree idx = TREE_OPERAND (arg, 1);
+	  if (TREE_CODE (idx) != INTEGER_CST)
 	    {
-	      /* If the offset and base aren't both constants, return 0.  */
-	      if (TREE_CODE (lower_bound) != INTEGER_CST)
-	        return 0;
-	      if (TREE_CODE (offset) != INTEGER_CST)
-		return 0;
-	      /* Adjust offset by the lower bound.  */
-	      offset = size_diffop (fold_convert (sizetype, offset),
-				    fold_convert (sizetype, lower_bound));
+	      /* Extract the variable index to prevent
+		 get_addr_base_and_unit_offset() from failing due to
+		 it.  Use it later to compute the non-constant offset
+		 into the string and return it to the caller.  */
+	      varidx = idx;
+	      ref = TREE_OPERAND (arg, 0);
 	    }
 	}
-      else if (TREE_CODE (TREE_OPERAND (arg, 0)) == MEM_REF)
-	{
-	  array = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
-	  offset = TREE_OPERAND (TREE_OPERAND (arg, 0), 1);
-	  if (TREE_CODE (array) != ADDR_EXPR)
-	    return 0;
-	  array = TREE_OPERAND (array, 0);
-	  if (TREE_CODE (array) != STRING_CST && !VAR_P (array))
-	    return 0;
-	}
-      else
-	return 0;
+      array = get_addr_base_and_unit_offset (ref, &base_off);
+      if (!array
+	  || (TREE_CODE (array) != VAR_DECL
+	      && TREE_CODE (array) != CONST_DECL
+	      && TREE_CODE (array) != STRING_CST))
+	return NULL_TREE;
     }
   else if (TREE_CODE (arg) == PLUS_EXPR || TREE_CODE (arg) == POINTER_PLUS_EXPR)
     {
@@ -11331,62 +11317,106 @@ string_constant (tree arg, tree *ptr_offset)
       STRIP_NOPS (arg0);
       STRIP_NOPS (arg1);
 
-      if (TREE_CODE (arg0) == ADDR_EXPR
-	  && (TREE_CODE (TREE_OPERAND (arg0, 0)) == STRING_CST
-	      || TREE_CODE (TREE_OPERAND (arg0, 0)) == VAR_DECL))
+      if (TREE_CODE (arg0) == ADDR_EXPR)
+	;   /* Do nothing.  */
+      else if (TREE_CODE (arg1) == ADDR_EXPR)
+	std::swap (arg0, arg1);
+      else
+	return NULL_TREE;
+
+      tree offset;
+      if (tree str = string_constant (arg0, &offset))
 	{
-	  array = TREE_OPERAND (arg0, 0);
-	  offset = arg1;
+	  tree type = TREE_TYPE (arg1);
+	  *ptr_offset = fold_build2 (PLUS_EXPR, type, offset, arg1);
+	  return str;
 	}
-      else if (TREE_CODE (arg1) == ADDR_EXPR
-	       && (TREE_CODE (TREE_OPERAND (arg1, 0)) == STRING_CST
-		   || TREE_CODE (TREE_OPERAND (arg1, 0)) == VAR_DECL))
+      return NULL_TREE;
+    }
+  else if (DECL_P (arg))
+    array = arg;
+  else
+    return NULL_TREE;
+
+  tree offset = wide_int_to_tree (sizetype, base_off);
+  if (varidx)
+    {
+      if (tree eltsize = TYPE_SIZE_UNIT (TREE_TYPE (array)))
 	{
-	  array = TREE_OPERAND (arg1, 0);
-	  offset = arg0;
+	  /* Add the scaled variable index to the constant offset.  */
+	  tree eltoff = fold_build2 (MULT_EXPR, TREE_TYPE (offset),
+				     fold_convert (sizetype, varidx),
+				     eltsize);
+	  offset = fold_build2 (PLUS_EXPR, TREE_TYPE (offset), offset, eltoff);
 	}
       else
-	return 0;
+	return NULL_TREE;
     }
-  else
-    return 0;
 
   if (TREE_CODE (array) == STRING_CST)
     {
       *ptr_offset = fold_convert (sizetype, offset);
       return array;
     }
-  else if (VAR_P (array) || TREE_CODE (array) == CONST_DECL)
-    {
-      int length;
-      tree init = ctor_for_folding (array);
 
-      /* Variables initialized to string literals can be handled too.  */
-      if (init == error_mark_node
-	  || !init
-	  || TREE_CODE (init) != STRING_CST)
-	return 0;
+  if (!VAR_P (array) && TREE_CODE (array) != CONST_DECL)
+    return NULL_TREE;
 
-      /* Avoid const char foo[4] = "abcde";  */
-      if (DECL_SIZE_UNIT (array) == NULL_TREE
-	  || TREE_CODE (DECL_SIZE_UNIT (array)) != INTEGER_CST
-	  || (length = TREE_STRING_LENGTH (init)) <= 0
-	  || compare_tree_int (DECL_SIZE_UNIT (array), length) < 0)
-	return 0;
+  tree init = ctor_for_folding (array);
 
-      /* If variable is bigger than the string literal, OFFSET must be constant
-	 and inside of the bounds of the string literal.  */
-      offset = fold_convert (sizetype, offset);
-      if (compare_tree_int (DECL_SIZE_UNIT (array), length) > 0
-	  && (! tree_fits_uhwi_p (offset)
-	      || compare_tree_int (offset, length) >= 0))
-	return 0;
+  /* Handle variables initialized with string literals.  */
+  if (!init || init == error_mark_node)
+    return NULL_TREE;
+  if (TREE_CODE (init) == CONSTRUCTOR)
+    {
+      if (TREE_CODE (arg) != ARRAY_REF
+	  && TREE_CODE (arg) == COMPONENT_REF
+	  && TREE_CODE (arg) == MEM_REF)
+	return NULL_TREE;
+
+      /* Convert the 64-bit constant offset to a wider type to avoid
+	 overflow.  */
+      offset_int wioff;
+      if (!base_off.is_constant (&wioff))
+	return NULL_TREE;
 
-      *ptr_offset = offset;
-      return init;
+      wioff *= BITS_PER_UNIT;
+      if (!wi::fits_uhwi_p (wioff))
+	return NULL_TREE;
+
+      base_off = wioff.to_uhwi ();
+      unsigned HOST_WIDE_INT fieldoff = 0;
+      init = fold_ctor_reference (NULL_TREE, init, base_off, 0, array,
+				  &fieldoff);
+      HOST_WIDE_INT cstoff;
+      if (init && base_off.is_constant (&cstoff))
+	{
+	  cstoff = (cstoff - fieldoff) / BITS_PER_UNIT;
+	  offset = build_int_cst (sizetype, cstoff);
+	}
     }
 
-  return 0;
+  if (!init || TREE_CODE (init) != STRING_CST)
+    return NULL_TREE;
+
+  tree array_size = DECL_SIZE_UNIT (array);
+  if (!array_size || TREE_CODE (array_size) != INTEGER_CST)
+    return NULL_TREE;
+
+  /* Avoid returning a string that doesn't fit in the array
+     it is stored in, like
+     const char a[4] = "abcde";
+     but do handle those that fit even if they have excess
+     initializers, such as in
+     const char a[4] = "abc\000\000";
+     The excess elements contribute to TREE_STRING_LENGTH()
+     but not to strlen().  */
+  unsigned HOST_WIDE_INT length = strlen (TREE_STRING_POINTER (init));
+  if (compare_tree_int (array_size, length + 1) < 0)
+    return NULL_TREE;
+
+  *ptr_offset = offset;
+  return init;
 }
 
 /* Generate code to calculate OPS, and exploded expression
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 6ce34bf..47ad0aa 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -6476,14 +6476,19 @@ get_base_constructor (tree base, poly_int64_pod *bit_offset,
     }
 }
 
-/* CTOR is CONSTRUCTOR of an array type.  Fold reference of type TYPE and size
-   SIZE to the memory at bit OFFSET.  */
+/* CTOR is CONSTRUCTOR of an array type.  Fold a reference of SIZE bits
+   to the memory at bit OFFSET.     When non-null, TYPE is the expected
+   type of the reference; otherwise the type of the referenced element
+   is used instead. When SIZE is zero, attempt to fold a reference to
+   the entire element which OFFSET refers to.  Increment *SUBOFF by
+   the bit offset of the accessed element.  */
 
 static tree
 fold_array_ctor_reference (tree type, tree ctor,
 			   unsigned HOST_WIDE_INT offset,
 			   unsigned HOST_WIDE_INT size,
-			   tree from_decl)
+			   tree from_decl,
+			   unsigned HOST_WIDE_INT *suboff)
 {
   offset_int low_bound;
   offset_int elt_size;
@@ -6508,12 +6513,13 @@ fold_array_ctor_reference (tree type, tree ctor,
     return NULL_TREE;
   elt_size = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor))));
 
-  /* We can handle only constantly sized accesses that are known to not
-     be larger than size of array element.  */
-  if (!TYPE_SIZE_UNIT (type)
-      || TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST
-      || elt_size < wi::to_offset (TYPE_SIZE_UNIT (type))
-      || elt_size == 0)
+  /* When TYPE is non-null, verify that it specifies a constant-sized
+     accessed not larger than size of array element.  */
+  if (type
+      && (!TYPE_SIZE_UNIT (type)
+	  || TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST
+	  || elt_size < wi::to_offset (TYPE_SIZE_UNIT (type))
+	  || elt_size == 0))
     return NULL_TREE;
 
   /* Compute the array index we look for.  */
@@ -6529,21 +6535,42 @@ fold_array_ctor_reference (tree type, tree ctor,
   if (inner_offset + size > elt_size.to_uhwi () * BITS_PER_UNIT)
     return NULL_TREE;
   if (tree val = get_array_ctor_element_at_index (ctor, access_index))
-    return fold_ctor_reference (type, val, inner_offset, size, from_decl);
+    {
+      if (!size && TREE_CODE (val) != CONSTRUCTOR)
+	{
+	  /* For the final reference to the entire accessed element
+	     (SIZE is zero), reset INNER_OFFSET, disegard TYPE (which
+	     may be null) in favor of the type of the element, and set
+	     SIZE to the size of the accessed element.  */
+	  inner_offset = 0;
+	  type = TREE_TYPE (val);
+	  size = elt_size.to_uhwi () * BITS_PER_UNIT;
+	}
+
+      *suboff += (access_index * elt_size * BITS_PER_UNIT).to_uhwi ();
+      return fold_ctor_reference (type, val, inner_offset, size, from_decl,
+				  suboff);
+    }
 
-  /* When memory is not explicitely mentioned in constructor,
-     it is 0 (or out of range).  */
-  return build_zero_cst (type);
+  /* Memory not explicitly mentioned in constructor is 0 (or
+     the reference is out of range).  */
+  return type ? build_zero_cst (type) : NULL_TREE;
 }
 
-/* CTOR is CONSTRUCTOR of an aggregate or vector.
-   Fold reference of type TYPE and size SIZE to the memory at bit OFFSET.  */
+/* CTOR is CONSTRUCTOR of an aggregate or vector.  Fold a reference
+   of SIZE bits to the memory at bit OFFSET.   When non-null, TYPE
+   is the expected type of the reference; otherwise the type of
+   the referenced member is used instead.  When SIZE is zero,
+   attempt to fold a reference to the entire member which OFFSET
+   refers to; in this case.  Increment *SUBOFF by the bit offset
+   of the accessed member.  */
 
 static tree
 fold_nonarray_ctor_reference (tree type, tree ctor,
 			      unsigned HOST_WIDE_INT offset,
 			      unsigned HOST_WIDE_INT size,
-			      tree from_decl)
+			      tree from_decl,
+			      unsigned HOST_WIDE_INT *suboff)
 {
   unsigned HOST_WIDE_INT cnt;
   tree cfield, cval;
@@ -6554,8 +6581,17 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
       tree byte_offset = DECL_FIELD_OFFSET (cfield);
       tree field_offset = DECL_FIELD_BIT_OFFSET (cfield);
       tree field_size = DECL_SIZE (cfield);
-      offset_int bitoffset;
-      offset_int bitoffset_end, access_end;
+
+      if (!field_size && TREE_CODE (cval) == STRING_CST)
+	{
+	  /* Determine the size of the flexible array member from
+	     the size of the string initializer provided for it.  */
+	  /* unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval); */
+	  /* tree eltype = TREE_TYPE (TREE_TYPE (cval)); */
+	  /* len *= tree_to_uhwi (TYPE_SIZE (eltype)); */
+	  /* field_size = build_int_cst (size_type_node, len); */
+	  field_size = TYPE_SIZE (TREE_TYPE (cval));
+	}
 
       /* Variable sized objects in static constructors makes no sense,
 	 but field_size can be NULL for flexible array members.  */
@@ -6566,50 +6602,82 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
 		      : TREE_CODE (TREE_TYPE (cfield)) == ARRAY_TYPE));
 
       /* Compute bit offset of the field.  */
-      bitoffset = (wi::to_offset (field_offset)
-		   + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT));
+      offset_int bitoffset
+	= (wi::to_offset (field_offset)
+	   + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT));
       /* Compute bit offset where the field ends.  */
+      offset_int bitoffset_end;
       if (field_size != NULL_TREE)
 	bitoffset_end = bitoffset + wi::to_offset (field_size);
       else
 	bitoffset_end = 0;
 
-      access_end = offset_int (offset) + size;
+      /* Compute the bit offset of the end of the desired access.
+	 As a special case, if the size of the desired access is
+	 zero, assume the access is to the entire field (and let
+	 the caller make any necessary adjustments by storing
+	 the actual bounds of the field in FIELDBOUNDS).  */
+      offset_int access_end = offset_int (offset);
+      if (size)
+	access_end += size;
+      else
+	access_end = bitoffset_end;
 
-      /* Is there any overlap between [OFFSET, OFFSET+SIZE) and
-	 [BITOFFSET, BITOFFSET_END)?  */
+      /* Is there any overlap between the desired access at
+	 [OFFSET, OFFSET+SIZE) and the offset of the field within
+	 the object at [BITOFFSET, BITOFFSET_END)?  */
       if (wi::cmps (access_end, bitoffset) > 0
 	  && (field_size == NULL_TREE
 	      || wi::lts_p (offset, bitoffset_end)))
 	{
-	  offset_int inner_offset = offset_int (offset) - bitoffset;
-	  /* We do have overlap.  Now see if field is large enough to
-	     cover the access.  Give up for accesses spanning multiple
-	     fields.  */
+	  *suboff += bitoffset.to_uhwi ();
+
+	  if (!size && TREE_CODE (cval) != CONSTRUCTOR)
+	    {
+	      /* For the final reference to the entire accessed member
+		 (SIZE is zero), reset OFFSET, disegard TYPE (which may
+		 be null) in favor of the type of the member, and set
+		 SIZE to the size of the accessed member.  */
+	      offset = bitoffset.to_uhwi ();
+	      type = TREE_TYPE (cval);
+	      size = (bitoffset_end - bitoffset).to_uhwi ();
+	    }
+
+	  /* We do have overlap.  Now see if the field is large enough
+	     to cover the access.  Give up for accesses that extend
+	     beyond the end of the object or that span multiple fields.  */
 	  if (wi::cmps (access_end, bitoffset_end) > 0)
 	    return NULL_TREE;
 	  if (offset < bitoffset)
 	    return NULL_TREE;
+
+	  offset_int inner_offset = offset_int (offset) - bitoffset;
 	  return fold_ctor_reference (type, cval,
 				      inner_offset.to_uhwi (), size,
-				      from_decl);
+				      from_decl, suboff);
 	}
     }
-  /* When memory is not explicitely mentioned in constructor, it is 0.  */
-  return build_zero_cst (type);
+  /* Memory not explicitly mentioned in constructor is 0.  */
+  return type ? build_zero_cst (type) : NULL_TREE;
 }
 
-/* CTOR is value initializing memory, fold reference of type TYPE and
-   size POLY_SIZE to the memory at bit POLY_OFFSET.  */
+/* CTOR is value initializing memory.  Fold a reference of TYPE and
+   bit size POLY_SIZE to the memory at bit POLY_OFFSET.  When SIZE
+   is zero, attempt to fold a reference to the entire subobject
+   which OFFSET refers to.  This is used when folding accesses to
+   string members of aggregates.  When non-null, set *SUBOFF to
+   the bit offset of the accessed subobject.  */
 
 tree
-fold_ctor_reference (tree type, tree ctor, poly_uint64 poly_offset,
-		     poly_uint64 poly_size, tree from_decl)
+fold_ctor_reference (tree type, tree ctor, const poly_uint64 &poly_offset,
+		     const poly_uint64 &poly_size, tree from_decl,
+		     unsigned HOST_WIDE_INT *suboff /* = NULL */)
 {
   tree ret;
 
   /* We found the field with exact match.  */
-  if (useless_type_conversion_p (type, TREE_TYPE (ctor))
+  if (type
+      && useless_type_conversion_p (type, TREE_TYPE (ctor))
       && known_eq (poly_offset, 0U))
     return canonicalize_constructor_val (unshare_expr (ctor), from_decl);
 
@@ -6650,14 +6718,17 @@ fold_ctor_reference (tree type, tree ctor, poly_uint64 poly_offset,
     }
   if (TREE_CODE (ctor) == CONSTRUCTOR)
     {
+      unsigned HOST_WIDE_INT dummy = 0;
+      if (!suboff)
+	suboff = &dummy;
 
       if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE
 	  || TREE_CODE (TREE_TYPE (ctor)) == VECTOR_TYPE)
 	return fold_array_ctor_reference (type, ctor, offset, size,
-					  from_decl);
-      else
-	return fold_nonarray_ctor_reference (type, ctor, offset, size,
-					     from_decl);
+					  from_decl, suboff);
+
+      return fold_nonarray_ctor_reference (type, ctor, offset, size,
+					   from_decl, suboff);
     }
 
   return NULL_TREE;
diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h
index 0a28ec7..04e9bfa 100644
--- a/gcc/gimple-fold.h
+++ b/gcc/gimple-fold.h
@@ -45,7 +45,9 @@ extern tree follow_all_ssa_edges (tree);
 extern tree gimple_fold_stmt_to_constant_1 (gimple *, tree (*) (tree),
 					    tree (*) (tree) = no_follow_ssa_edges);
 extern tree gimple_fold_stmt_to_constant (gimple *, tree (*) (tree));
-extern tree fold_ctor_reference (tree, tree, poly_uint64, poly_uint64, tree);
+extern tree fold_ctor_reference (tree, tree, const poly_uint64&,
+				 const poly_uint64&, tree,
+				 unsigned HOST_WIDE_INT * = NULL);
 extern tree fold_const_aggregate_ref_1 (tree, tree (*) (tree));
 extern tree fold_const_aggregate_ref (tree);
 extern tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree,
diff --git a/gcc/testsuite/gcc.dg/strlenopt-49.c b/gcc/testsuite/gcc.dg/strlenopt-49.c
new file mode 100644
index 0000000..03e063b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-49.c
@@ -0,0 +1,288 @@
+/* PR tree-optimization/77357 - strlen of constant strings not folded
+   { dg-do compile }
+   { dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-ccp" } */
+
+#include "strlenopt.h"
+
+#define CONCAT(x, y) x ## y
+#define CAT(x, y) CONCAT (x, y)
+#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do {				\
+    extern void FAILNAME (name) (void);		\
+    FAILNAME (name)();				\
+  } while (0)
+
+/* Macro to emit a call to funcation named
+     call_in_true_branch_not_eliminated_on_line_NNN()
+   for each call that's expected to be eliminated.  The dg-final
+   scan-tree-dump-time directive at the bottom of the test verifies
+   that no such call appears in output.  */
+#define ELIM(expr) \
+  if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+#define T(s, n) ELIM (strlen (s) == n)
+
+
+struct S
+{
+  char a1[1], a2[2], a3[3], a4[4], a5[5], a6[6], a7[7], a8[8], a9[9];
+};
+
+#define S0 ""
+#define S1 "1"
+#define S2 "12"
+#define S3 "123"
+#define S4 "1234"
+#define S5 "12345"
+#define S6 "123456"
+#define S7 "1234567"
+#define S8 "12345678"
+
+const char a9[][9] = { S0, S1, S2, S3, S4, S5, S6, S7, S8 };
+
+const char a_1_9[1][9] = { S8 };
+const char a_2_9[2][9] = { S8, S7};
+
+const char a9_9[][9][9] = {
+  { S0, S0, S0, S0, S0, S0, S0, S0, S0 },
+  { S0, S1, S1, S1, S1, S1, S1, S1, S1 },
+  { S0, S1, S2, S2, S2, S2, S2, S2, S2 },
+  { S0, S1, S2, S3, S3, S3, S3, S3, S3 },
+  { S0, S1, S2, S3, S4, S4, S4, S4, S4 },
+  { S0, S1, S2, S3, S4, S5, S5, S5, S5 },
+  { S0, S1, S2, S3, S4, S5, S6, S6, S6 },
+  { S0, S1, S2, S3, S4, S5, S6, S7, S7 },
+  { S0, S1, S2, S3, S4, S5, S6, S7, S8 }
+};
+
+const struct S s = { S0, S1, S2, S3, S4, S5, S6, S7, S8 };
+
+const struct S sa[9] = {
+  { S0, S0, S0, S0, S0, S0, S0, S0, S0 },
+  { S0, S1, S1, S1, S1, S1, S1, S1, S1 },
+  { S0, S1, S2, S2, S2, S2, S2, S2, S2 },
+  { S0, S1, S2, S3, S3, S3, S3, S3, S3 },
+  { S0, S1, S2, S3, S4, S4, S4, S4, S4 },
+  { S0, S1, S2, S3, S4, S5, S5, S5, S5 },
+  { S0, S1, S2, S3, S4, S5, S6, S6, S6 },
+  { S0, S1, S2, S3, S4, S5, S6, S7, S7 },
+  { S0, S1, S2, S3, S4, S5, S6, S7, S8 }
+};
+
+const struct S sa3_5_7[3][5][7] = {
+  [1][2][3].a2 = S1, [1][3][5].a3 = S2, [2][4][5].a4 = S3
+};
+
+
+void test_global_array (void)
+{
+  T (a9[0], 0);  T (a9[0] + 0, 0);  T (a9[0] + 0, 0);  T (a9[0] + 0, 0);
+  T (a9[1], 1);  T (a9[1] + 1, 0);  T (a9[1] + 1, 0);  T (a9[1] + 1, 0);
+  T (a9[2], 2);
+  T (a9[2] + 1, 1);
+  T (a9[2] + 2, 0);
+  T (a9[2] + 2, 0);
+
+  T (a9[3], 3);  T (a9[3] + 1, 2);  T (a9[3] + 2, 1);  T (a9[3] + 3, 0);
+  T (a9[4], 4);  T (a9[4] + 1, 3);  T (a9[4] + 2, 2);  T (a9[4] + 3, 1);
+  T (a9[5], 5);  T (a9[5] + 1, 4);  T (a9[5] + 2, 3);  T (a9[5] + 3, 2);
+  T (a9[6], 6);  T (a9[6] + 1, 5);  T (a9[6] + 2, 4);  T (a9[6] + 3, 3);
+  T (a9[7], 7);  T (a9[7] + 1, 6);  T (a9[7] + 2, 5);  T (a9[7] + 3, 4);
+  T (a9[8], 8);  T (a9[8] + 1, 7);  T (a9[8] + 2, 6);  T (a9[8] + 3, 5);
+
+  T (a_1_9[0], 8);
+  T (a_1_9[0] + 1, 7);
+  T (a_1_9[0] + 7, 1);
+  T (a_1_9[0] + 8, 0);
+
+  T (a_2_9[0], 8);
+  T (a_2_9[0] + 1, 7);
+  T (a_2_9[0] + 7, 1);
+  T (a_2_9[0] + 8, 0);
+
+  T (a_2_9[1], 7);
+  T (a_2_9[1] + 1, 6);
+  T (a_2_9[1] + 6, 1);
+  T (a_2_9[1] + 7, 0);
+  T (a_2_9[1] + 8, 0);
+}
+
+void test_global_array_array (void)
+{
+  T (a9_9[0][0], 0);  T (a9_9[1][0], 0);  T (a9_9[2][0], 0);
+  T (a9_9[0][1], 0);  T (a9_9[1][1], 1);  T (a9_9[2][1], 1);
+  T (a9_9[0][2], 0);  T (a9_9[1][2], 1);  T (a9_9[2][2], 2);
+  T (a9_9[0][3], 0);  T (a9_9[1][3], 1);  T (a9_9[2][3], 2);
+  T (a9_9[0][4], 0);  T (a9_9[1][4], 1);  T (a9_9[2][4], 2);
+  T (a9_9[0][5], 0);  T (a9_9[1][5], 1);  T (a9_9[2][5], 2);
+  T (a9_9[0][6], 0);  T (a9_9[1][6], 1);  T (a9_9[2][6], 2);
+  T (a9_9[0][7], 0);  T (a9_9[1][7], 1);  T (a9_9[2][7], 2);
+  T (a9_9[0][8], 0);  T (a9_9[1][8], 1);  T (a9_9[2][8], 2);
+
+  T (a9_9[3][0], 0);  T (a9_9[4][0], 0);  T (a9_9[5][0], 0);
+  T (a9_9[3][1], 1);  T (a9_9[4][1], 1);  T (a9_9[5][1], 1);
+  T (a9_9[3][2], 2);  T (a9_9[4][2], 2);  T (a9_9[5][2], 2);
+  T (a9_9[3][3], 3);  T (a9_9[4][3], 3);  T (a9_9[5][3], 3);
+  T (a9_9[3][4], 3);  T (a9_9[4][4], 4);  T (a9_9[5][4], 4);
+  T (a9_9[3][5], 3);  T (a9_9[4][5], 4);  T (a9_9[5][5], 5);
+  T (a9_9[3][6], 3);  T (a9_9[4][6], 4);  T (a9_9[5][6], 5);
+  T (a9_9[3][7], 3);  T (a9_9[4][7], 4);  T (a9_9[5][7], 5);
+  T (a9_9[3][8], 3);  T (a9_9[4][8], 4);  T (a9_9[5][8], 5);
+
+  T (a9_9[6][0], 0);  T (a9_9[7][0], 0);  T (a9_9[8][0], 0);
+  T (a9_9[6][1], 1);  T (a9_9[7][1], 1);  T (a9_9[8][1], 1);
+  T (a9_9[6][2], 2);  T (a9_9[7][2], 2);  T (a9_9[8][2], 2);
+  T (a9_9[6][3], 3);  T (a9_9[7][3], 3);  T (a9_9[8][3], 3);
+  T (a9_9[6][4], 4);  T (a9_9[7][4], 4);  T (a9_9[8][4], 4);
+  T (a9_9[6][5], 5);  T (a9_9[7][5], 5);  T (a9_9[8][5], 5);
+  T (a9_9[6][6], 6);  T (a9_9[7][6], 6);  T (a9_9[8][6], 6);
+  T (a9_9[6][7], 6);  T (a9_9[7][7], 7);  T (a9_9[8][7], 7);
+  T (a9_9[6][8], 6);  T (a9_9[7][8], 7);  T (a9_9[8][8], 8);
+
+
+  T (a9_9[0][0] + 1, 0);  T (a9_9[1][0] + 1, 0);  T (a9_9[2][0] + 2, 0);
+  T (a9_9[0][1] + 2, 0);  T (a9_9[1][1] + 1, 0);  T (a9_9[2][1] + 2, 0);
+  T (a9_9[0][2] + 3, 0);  T (a9_9[1][2] + 1, 0);  T (a9_9[2][2] + 2, 0);
+  T (a9_9[0][3] + 4, 0);  T (a9_9[1][3] + 1, 0);  T (a9_9[2][3] + 2, 0);
+  T (a9_9[0][4] + 5, 0);  T (a9_9[1][4] + 1, 0);  T (a9_9[2][4] + 2, 0);
+  T (a9_9[0][5] + 6, 0);  T (a9_9[1][5] + 1, 0);  T (a9_9[2][5] + 2, 0);
+  T (a9_9[0][6] + 7, 0);  T (a9_9[1][6] + 1, 0);  T (a9_9[2][6] + 2, 0);
+  T (a9_9[0][7] + 8, 0);  T (a9_9[1][7] + 1, 0);  T (a9_9[2][7] + 2, 0);
+}
+
+void test_global_struct (void)
+{
+  T (s.a1, 0);
+  T (s.a2, 1);
+  T (s.a3, 2);
+  T (s.a4, 3);
+  T (s.a5, 4);
+  T (s.a6, 5);
+  T (s.a7, 6);
+  T (s.a8, 7);
+  T (s.a9, 8);
+}
+
+void test_global_struct_array (void)
+{
+  T (sa[0].a1, 0);  T (sa[1].a1, 0);  T (sa[2].a1, 0);  T (sa[3].a1, 0);
+  T (sa[0].a2, 0);  T (sa[1].a2, 1);  T (sa[2].a2, 1);  T (sa[3].a2, 1);
+  T (sa[0].a3, 0);  T (sa[1].a3, 1);  T (sa[2].a3, 2);  T (sa[3].a3, 2);
+  T (sa[0].a4, 0);  T (sa[1].a4, 1);  T (sa[2].a4, 2);  T (sa[3].a4, 3);
+  T (sa[0].a5, 0);  T (sa[1].a5, 1);  T (sa[2].a5, 2);  T (sa[3].a5, 3);
+  T (sa[0].a6, 0);  T (sa[1].a6, 1);  T (sa[2].a6, 2);  T (sa[3].a6, 3);
+  T (sa[0].a7, 0);  T (sa[1].a7, 1);  T (sa[2].a7, 2);  T (sa[3].a7, 3);
+  T (sa[0].a8, 0);  T (sa[1].a8, 1);  T (sa[2].a8, 2);  T (sa[3].a8, 3);
+  T (sa[0].a9, 0);  T (sa[1].a9, 1);  T (sa[2].a9, 2);  T (sa[3].a9, 3);
+
+  T (sa[4].a1, 0);  T (sa[5].a1, 0);  T (sa[6].a1, 0);  T (sa[7].a1, 0);
+  T (sa[4].a2, 1);  T (sa[5].a2, 1);  T (sa[6].a2, 1);  T (sa[7].a2, 1);
+  T (sa[4].a3, 2);  T (sa[5].a3, 2);  T (sa[6].a3, 2);  T (sa[7].a3, 2);
+  T (sa[4].a4, 3);  T (sa[5].a4, 3);  T (sa[6].a4, 3);  T (sa[7].a4, 3);
+  T (sa[4].a5, 4);  T (sa[5].a5, 4);  T (sa[6].a5, 4);  T (sa[7].a5, 4);
+  T (sa[4].a6, 4);  T (sa[5].a6, 5);  T (sa[6].a6, 5);  T (sa[7].a6, 5);
+  T (sa[4].a7, 4);  T (sa[5].a7, 5);  T (sa[6].a7, 6);  T (sa[7].a7, 6);
+  T (sa[4].a8, 4);  T (sa[5].a8, 5);  T (sa[6].a8, 6);  T (sa[7].a8, 7);
+  T (sa[4].a9, 4);  T (sa[5].a9, 5);  T (sa[6].a9, 6);  T (sa[7].a9, 7);
+
+  T (sa[8].a1, 0);
+  T (sa[8].a2, 1);  T (sa[8].a2 + 1, 0);
+  T (sa[8].a3, 2);  T (sa[8].a3 + 1, 1);  T (sa[8].a3 + 2, 0);
+  T (sa[8].a4, 3);  T (sa[8].a4 + 1, 2);  T (sa[8].a4 + 2, 1);
+  T (sa[8].a5, 4);  T (sa[8].a5 + 1, 3);  T (sa[8].a5 + 2, 2);
+  T (sa[8].a6, 5);  T (sa[8].a6 + 1, 4);  T (sa[8].a6 + 2, 3);
+  T (sa[8].a7, 6);  T (sa[8].a7 + 1, 5);  T (sa[8].a7 + 2, 4);
+  T (sa[8].a8, 7);  T (sa[8].a8 + 1, 6);  T (sa[8].a8 + 2, 5);
+  T (sa[8].a9, 8);  T (sa[8].a9 + 1, 7);  T (sa[8].a9 + 2, 6);
+
+
+  T (sa3_5_7[1][2][3].a2, 1);
+  T (sa3_5_7[1][3][5].a3, 2);
+  T (sa3_5_7[2][4][5].a4, 3);
+
+  T (sa3_5_7[0][0][0].a1, 0);
+  T (sa3_5_7[0][0][0].a2, 0);
+  T (sa3_5_7[0][0][0].a3, 0);
+  T (sa3_5_7[0][0][0].a4, 0);
+  T (sa3_5_7[0][0][0].a5, 0);
+  T (sa3_5_7[0][0][0].a6, 0);
+  T (sa3_5_7[0][0][0].a7, 0);
+  T (sa3_5_7[0][0][0].a8, 0);
+  T (sa3_5_7[0][0][0].a9, 0);
+
+  T (sa3_5_7[0][0][1].a1, 0);
+  T (sa3_5_7[0][0][1].a2, 0);
+  T (sa3_5_7[0][0][1].a3, 0);
+  T (sa3_5_7[0][0][1].a4, 0);
+  T (sa3_5_7[0][0][1].a5, 0);
+  T (sa3_5_7[0][0][1].a6, 0);
+  T (sa3_5_7[0][0][1].a7, 0);
+  T (sa3_5_7[0][0][1].a8, 0);
+  T (sa3_5_7[0][0][1].a9, 0);
+
+  T (sa3_5_7[0][1][0].a1, 0);
+  T (sa3_5_7[0][1][0].a2, 0);
+  T (sa3_5_7[0][1][0].a3, 0);
+  T (sa3_5_7[0][1][0].a4, 0);
+  T (sa3_5_7[0][1][0].a5, 0);
+  T (sa3_5_7[0][1][0].a6, 0);
+  T (sa3_5_7[0][1][0].a7, 0);
+  T (sa3_5_7[0][1][0].a8, 0);
+  T (sa3_5_7[0][1][0].a9, 0);
+
+  T (sa3_5_7[1][0][0].a1, 0);
+  T (sa3_5_7[1][0][0].a2, 0);
+  T (sa3_5_7[1][0][0].a3, 0);
+  T (sa3_5_7[1][0][0].a4, 0);
+  T (sa3_5_7[1][0][0].a5, 0);
+  T (sa3_5_7[1][0][0].a6, 0);
+  T (sa3_5_7[1][0][0].a7, 0);
+  T (sa3_5_7[1][0][0].a8, 0);
+  T (sa3_5_7[1][0][0].a9, 0);
+}
+
+
+struct SS {
+  char a9[9][9];
+  struct S sa9[9];
+};
+
+const struct SS ssa[] = {
+  [1] = {
+    .a9 = { [3] = S3, [7] = S7 },
+    .sa9 = { [5] = { .a5 = S4, .a7 = S6 } }
+  },
+  [5] = {
+    .a9 = { [1] = S8, [5] = S4 },
+    .sa9 = { [3] = { .a3 = S2, .a6 = S3 } }
+  }
+};
+
+void test_global_struct_struct_array (void)
+{
+  T (ssa[0].a9[0], 0);
+  T (ssa[0].a9[3], 0);
+  T (ssa[0].sa9[5].a5, 0);
+  T (ssa[0].sa9[5].a7, 0);
+
+  T (ssa[1].a9[0], 0);
+
+  T (ssa[1].a9[3], 3);
+  T (ssa[1].a9[7], 7);
+  T (ssa[1].sa9[5].a5, 4);
+  T (ssa[1].sa9[5].a7, 6);
+
+  T (ssa[2].a9[3], 0);
+  T (ssa[2].a9[7], 0);
+  T (ssa[2].sa9[5].a5, 0);
+  T (ssa[2].sa9[5].a7, 0);
+
+  T (ssa[5].a9[1], 8);
+  T (ssa[5].a9[5], 4);
+  T (ssa[5].sa9[3].a3, 2);
+  T (ssa[5].sa9[3].a6, 3);
+}
+
+/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
+   { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-50.c b/gcc/testsuite/gcc.dg/strlenopt-50.c
new file mode 100644
index 0000000..1d1d368
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-50.c
@@ -0,0 +1,116 @@
+/* PR tree-optimization/86415 - strlen() not folded for substrings
+   within constant arrays
+   { dg-do compile }
+   { dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-ccp" } */
+
+#include "strlenopt.h"
+
+#define CONCAT(x, y) x ## y
+#define CAT(x, y) CONCAT (x, y)
+#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do {				\
+    extern void FAILNAME (name) (void);		\
+    FAILNAME (name)();				\
+  } while (0)
+
+/* Macro to emit a call to funcation named
+     call_in_true_branch_not_eliminated_on_line_NNN()
+   for each call that's expected to be eliminated.  The dg-final
+   scan-tree-dump-time directive at the bottom of the test verifies
+   that no such call appears in output.  */
+#define ELIM(expr) \
+  if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+#define T(s, n) ELIM (strlen (s) == n)
+
+/*                              11111
+	     0 1  23 4  567 8  901234  */
+#define STR "1\00012\000123\0001234\0"
+
+const char a[]   = STR;
+const char b[20] = STR;
+
+void test_literal (void)
+{
+  /* Verify that strlen() of substrings within a string literal are
+     correctly folded.  */
+  T (STR,      1);  T (STR +  1, 0);  T (STR +  2, 2);  T (STR +  3, 1);
+  T (STR +  4, 0);  T (STR +  5, 3);  T (STR +  6, 2);  T (STR +  7, 1);
+  T (STR +  8, 0);  T (STR +  9, 4);  T (STR + 10, 3);  T (STR + 11, 2);
+  T (STR + 12, 1);  T (STR + 13, 0);  T (STR + 14, 0);
+
+  T (&(STR[0]),  1);  T (&(STR[ 1]), 0);  T (&(STR[ 2]), 2);
+  T (&(STR[ 3]), 1);  T (&(STR[ 4]), 0);  T (&(STR[ 5]), 3);
+  T (&(STR[ 6]), 2);  T (&(STR[ 7]), 1);  T (&(STR[ 8]), 0);
+  T (&(STR[ 9]), 4);  T (&(STR[10]), 3);  T (&(STR[11]), 2);
+  T (&(STR[12]), 1);  T (&(STR[13]), 0);  T (&(STR[14]), 0);
+
+  T (&(STR[0])  +  1, 0);  T (&(STR[ 1]) +  1, 2);  T (&(STR[ 2]) +  1, 1);
+  T (&(STR[ 3]) +  1, 0);  T (&(STR[ 4]) +  1, 3);  T (&(STR[ 5]) +  1, 2);
+  T (&(STR[ 6]) +  1, 1);  T (&(STR[ 7]) +  1, 0);  T (&(STR[ 8]) +  1, 4);
+  T (&(STR[ 9]) +  1, 3);  T (&(STR[10]) +  1, 2);  T (&(STR[11]) +  1, 1);
+  T (&(STR[12]) +  1, 0);  T (&(STR[13]) +  1, 0);  T (&(STR[13]) - 13, 1);
+  T (&(STR[13]) - 12, 0);  T (&(STR[13]) - 11, 2);  T (&(STR[13]) - 10, 1);
+}
+
+void test_array (void)
+{
+  /* Verify that strlen() of substrings within a fully initialized
+     array are correctly folded.  */
+  T (a,      1);  T (a +  1, 0);  T (a +  2, 2);  T (a +  3, 1);
+  T (a +  4, 0);  T (a +  5, 3);  T (a +  6, 2);  T (a +  7, 1);
+  T (a +  8, 0);  T (a +  9, 4);  T (a + 10, 3);  T (a + 11, 2);
+  T (a + 12, 1);  T (a + 13, 0);  T (a + 14, 0);
+
+  /* Verify that strlen() of substrings within a partially initialized
+     array are also correctly folded, including those referring to
+     the empty substrings in the implicitly initialized elements.  */
+  T (b,      1);  T (b +  1, 0);  T (b +  2, 2);  T (b +  3, 1);
+  T (b +  4, 0);  T (b +  5, 3);  T (b +  6, 2);  T (b +  7, 1);
+  T (b +  8, 0);  T (b +  9, 4);  T (b + 10, 3);  T (b + 11, 2);
+  T (b + 12, 1);  T (b + 13, 0);  T (b + 14, 0);  T (b + 15, 0);
+  T (b + 16, 0);  T (b + 17, 0);  T (b + 18, 0);  T (b + 19, 0);
+}
+
+void test_array_ref_plus (void)
+{
+  /* Verify that strlen() of substrings within a fully initialized
+     array referred to by array indices with offsets are correctly
+     folded.  */
+  T (&a[ 0],     1);  T (&a[ 0] + 1, 0);
+  T (&a[ 1],     0);  T (&a[ 1] + 1, 2);
+  T (&a[ 2],     2);  T (&a[ 2] + 1, 1);  T (&a[ 2] + 2, 0);
+  T (&a[ 3],     1);  T (&a[ 3] + 1, 0);
+  T (&a[ 4],     0);  T (&a[ 4] + 1, 3);
+  T (&a[ 5],     3);  T (&a[ 5] + 1, 2);
+  T (&a[ 5] + 2, 1);  T (&a[ 5] + 3, 0);  T (&a[ 5] + 4, 4);
+  T (&a[ 6],     2);  T (&a[ 6] + 1, 1);  T (&a[ 6] + 2, 0);
+  T (&a[ 7],     1);  T (&a[ 7] + 1, 0);
+  T (&a[ 8],     0);  T (&a[ 8] + 1, 4);
+  T (&a[ 9],     4);  T (&a[ 9] + 1, 3);  T (&a[ 9] + 2, 2);
+  T (&a[ 9] + 3, 1);  T (&a[ 9] + 4, 0);  T (&a[ 9] + 5, 0);
+  T (&a[10],     3);  T (&a[10] + 1, 2);  T (&a[10] + 2, 1);
+  T (&a[10] + 3, 0);  T (&a[10] + 4, 0);
+  T (&a[11],     2);  T (&a[11] + 1, 1);  T (&a[11] + 2, 0);
+  T (&a[12],     1);  T (&a[12] + 1, 0);  T (&a[12] + 2, 0);
+  T (&a[13],     0);  T (&a[13] + 1, 0);
+  T (&a[14],     0);
+}
+
+void test_array_ref (void)
+{
+  T (&a[ 0], 1);  T (&a[ 1], 0);  T (&a[ 2], 2);  T (&a[ 3], 1);
+  T (&a[ 4], 0);  T (&a[ 5], 3);  T (&a[ 6], 2);  T (&a[ 7], 1);
+  T (&a[ 8], 0);  T (&a[ 9], 4);  T (&a[10], 3);  T (&a[11], 2);
+  T (&a[12], 1);  T (&a[13], 0);  T (&a[14], 0);
+
+  T (&b[ 0], 1);  T (&b[ 1], 0);  T (&b[ 2], 2);  T (&b[ 3], 1);
+  T (&b[ 4], 0);  T (&b[ 5], 3);  T (&b[ 6], 2);  T (&b[ 7], 1);
+  T (&b[ 8], 0);  T (&b[ 9], 4);  T (&b[10], 3);  T (&b[11], 2);
+  T (&b[12], 1);  T (&b[13], 0);  T (&b[14], 0);  T (&b[15], 0);
+  T (&b[16], 0);  T (&b[17], 0);  T (&b[18], 0);  T (&b[19], 0);
+}
+
+/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
+   { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-51.c b/gcc/testsuite/gcc.dg/strlenopt-51.c
new file mode 100644
index 0000000..cbed11b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-51.c
@@ -0,0 +1,121 @@
+/* PR tree-optimization/77357 - strlen of constant strings not folded
+   { dg-do compile }
+   { dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+#define CONCAT(x, y) x ## y
+#define CAT(x, y) CONCAT (x, y)
+#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do {				\
+    extern void FAILNAME (name) (void);		\
+    FAILNAME (name)();				\
+  } while (0)
+
+/* Macro to emit a call to funcation named
+     call_in_true_branch_not_eliminated_on_line_NNN()
+   for each call that's expected to be eliminated.  The dg-final
+   scan-tree-dump-time directive at the bottom of the test verifies
+   that no such call appears in output.  */
+#define ELIM(expr) \
+  if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+/* Macro to emit a call to a function named
+     call_made_in_{true,false}_branch_on_line_NNN()
+   for each call that's expected to be retained.  The dg-final
+   scan-tree-dump-time directive at the bottom of the test verifies
+   that the expected number of both kinds of calls appears in output
+   (a pair for each line with the invocation of the KEEP() macro.  */
+#define KEEP(expr)				\
+  if (expr)					\
+    FAIL (made_in_true_branch);			\
+  else						\
+    FAIL (made_in_false_branch)
+
+#define T(s, n) ELIM (strlen (s) == n)
+
+
+struct S
+{
+  char a1[1], a2[2], a3[3], a4[4], a5[5], a6[6], a7[7], a8[8], a9[9];
+};
+
+#define S0 ""
+#define S1 "1"
+#define S2 "12"
+#define S3 "123"
+#define S4 "1234"
+#define S5 "12345"
+#define S6 "123456"
+#define S7 "1234567"
+#define S8 "12345678"
+
+const char a9[][9] = { S0, S1, S2, S3, S4, S5, S6, S7, S8 };
+
+void test_elim_a9 (int i)
+{
+  ELIM (strlen (&a9[0][i]) > 0);
+  ELIM (strlen (&a9[1][i]) > 1);
+  ELIM (strlen (&a9[2][i]) > 2);
+  ELIM (strlen (&a9[3][i]) > 3);
+  ELIM (strlen (&a9[4][i]) > 4);
+  ELIM (strlen (&a9[5][i]) > 5);
+  ELIM (strlen (&a9[6][i]) > 6);
+  ELIM (strlen (&a9[7][i]) > 7);
+  ELIM (strlen (&a9[8][i]) > 8);
+}
+
+const char a9_9[][9][9] = {
+  { S0, S1, S2, S3, S4, S5, S6, S7, S8 },
+  { S1, S2, S3, S4, S5, S6, S7, S8, S0 },
+  { S2, S3, S4, S5, S6, S7, S8, S0, S1 },
+  { S3, S4, S5, S6, S7, S8, S0, S1, S2 },
+  { S4, S5, S6, S7, S8, S0, S1, S2, S3 },
+  { S5, S6, S7, S8, S0, S1, S2, S3, S4 },
+  { S6, S7, S8, S0, S1, S2, S3, S4, S5 },
+  { S7, S8, S0, S1, S2, S3, S4, S5, S6 },
+  { S8, S0, S2, S2, S3, S4, S5, S6, S7 }
+};
+
+void test_elim_a9_9 (int i)
+{
+#undef T
+#define T(I)					\
+  ELIM (strlen (&a9_9[I][0][i]) > (0 + I) % 9);	\
+  ELIM (strlen (&a9_9[I][1][i]) > (1 + I) % 9);	\
+  ELIM (strlen (&a9_9[I][2][i]) > (2 + i) % 9);	\
+  ELIM (strlen (&a9_9[I][3][i]) > (3 + I) % 9);	\
+  ELIM (strlen (&a9_9[I][4][i]) > (4 + I) % 9);	\
+  ELIM (strlen (&a9_9[I][5][i]) > (5 + I) % 9);	\
+  ELIM (strlen (&a9_9[I][6][i]) > (6 + I) % 9);	\
+  ELIM (strlen (&a9_9[I][7][i]) > (7 + I) % 9);	\
+  ELIM (strlen (&a9_9[I][8][i]) > (8 + I) % 9)
+
+  T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8);
+}
+
+#line 1000
+
+void test_keep_a9_9 (int i)
+{
+#undef T
+#define T(I)					\
+  KEEP (strlen (&a9_9[i][I][0]) > (1 + I) % 9);	\
+  KEEP (strlen (&a9_9[i][I][1]) > (1 + I) % 9);	\
+  KEEP (strlen (&a9_9[i][I][2]) > (2 + I) % 9);	\
+  KEEP (strlen (&a9_9[i][I][3]) > (3 + I) % 9);	\
+  KEEP (strlen (&a9_9[i][I][4]) > (4 + I) % 9);	\
+  KEEP (strlen (&a9_9[i][I][5]) > (5 + I) % 9);	\
+  KEEP (strlen (&a9_9[i][I][6]) > (6 + I) % 9);	\
+  KEEP (strlen (&a9_9[i][I][7]) > (7 + I) % 9);	\
+  KEEP (strlen (&a9_9[i][I][8]) > (8 + I) % 9)
+
+  T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8);
+}
+
+/* { dg-final { scan-tree-dump-times "strlen" 72 "gimple" } }
+   { dg-final { scan-tree-dump-times "strlen" 63 "optimized" } }
+
+   { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 72 "optimized" } }
+   { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 81 "optimized" } } */
Richard Biener July 9, 2018, 12:40 p.m. UTC | #3
On Sun, Jul 8, 2018 at 4:56 AM Martin Sebor <msebor@gmail.com> wrote:
>
> On 07/06/2018 09:52 AM, Richard Biener wrote:
> > On Fri, Jul 6, 2018 at 1:54 AM Martin Sebor <msebor@gmail.com> wrote:
> >>
> >> GCC folds accesses to members of constant aggregates except
> >> for character arrays/strings.  For example, the strlen() call
> >> below is not folded:
> >>
> >>    const char a[][4] = { "1", "12" };
> >>
> >>    int f (void) { retturn strlen (a[1]); }
> >>
> >> The attached change set enhances the string_constant() function
> >> to make it possible to extract string constants from aggregate
> >> initializers (CONSTRUCTORS).
> >>
> >> The initial solution was much simpler but as is often the case,
> >> MEM_REF made it fail to fold things like:
> >>
> >>    int f (void) { retturn strlen (a[1] + 1); }
> >>
> >> Handling those made the project a bit more interesting and
> >> the final solution somewhat more involved.
> >>
> >> To handle offsets into aggregate string members the patch also
> >> extends the fold_ctor_reference() function to extract entire
> >> string array initializers even if the offset points past
> >> the beginning of the string and even though the size and
> >> exact type of the reference are not known (there isn't enough
> >> information in a MEM_REF to determine that).
> >>
> >> Tested along with the patch for PR 86415 on x86_64-linux.
> >
> > +      if (TREE_CODE (init) == CONSTRUCTOR)
> > +       {
> > +         tree type;
> > +         if (TREE_CODE (arg) == ARRAY_REF
> > +             || TREE_CODE (arg) == MEM_REF)
> > +           type = TREE_TYPE (arg);
> > +         else if (TREE_CODE (arg) == COMPONENT_REF)
> > +           {
> > +             tree field = TREE_OPERAND (arg, 1);
> > +             type = TREE_TYPE (field);
> > +           }
> > +         else
> > +           return NULL_TREE;
> >
> > what's wrong with just
> >
> >     type = TREE_TYPE (field);
>
> In response to your comment below abut size I simplified things
> further so determining the type a priori is no longer necessary.
>
> > ?
> >
> > +         base_off *= BITS_PER_UNIT;
> >
> > poly_uint64 isn't enough for "bits", with wide-int you'd use offset_int,
> > for poly you'd then use poly_offset?
>
> Okay, I tried to avoid the overflow.  (Converting between all
> these flavors of wide int types is a monumental PITA.)
>
> >
> > You extend fold_ctor_reference to treat size == 0 specially but then
> > bother to compute a size here - that looks unneeded?
>
> Yes, well spotted, thanks!  I simplified the code so this isn't
> necessary, and neither is the type.
>
> >
> > While the offset of the reference determines the first field in the
> > CONSTRUCTOR, how do you know the access doesn't touch
> > adjacent ones?  STRING_CSTs do not have to be '\0' terminated,
> > so consider
> >
> >   char x[2][4] = { "abcd", "abcd" };
> >
> > and MEM[&x] with a char[8] type?  memcpy "inlining" will create
> > such MEMs for example.
>
> The code is only used to find string constants in initializer
> expressions where I don't think the size of the access comes
> into play.  If a memcpy() call results in a MEM_REF[char[8],
> &x, 8] that's fine.  It's a valid reference and we can still
> get the underlying character sequence (which is represented
> as two STRING_CSTs with the two string literals).  I might
> be missing the point of your question.

Maybe irrelevant for strlen folding depending on what you do
for missing '\0' termination.

> >
> > @@ -6554,8 +6577,16 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
> >        tree byte_offset = DECL_FIELD_OFFSET (cfield);
> >        tree field_offset = DECL_FIELD_BIT_OFFSET (cfield);
> >        tree field_size = DECL_SIZE (cfield);
> > -      offset_int bitoffset;
> > -      offset_int bitoffset_end, access_end;
> > +
> > +      if (!field_size && TREE_CODE (cval) == STRING_CST)
> > +       {
> > +         /* Determine the size of the flexible array member from
> > +            the size of the string initializer provided for it.  */
> > +         unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval);
> > +         tree eltype = TREE_TYPE (TREE_TYPE (cval));
> > +         len *= tree_to_uhwi (TYPE_SIZE (eltype));
> > +         field_size = build_int_cst (size_type_node, len);
> > +       }
> >
> > Why does this only apply to STRING_CST initializers and not CONSTRUCTORS,
> > say, for
> >
> > struct S { int i; int a[]; } s = { 1, { 2, 3, 4, 5, 6 } };
>
> I can't think of a use for it.  Do you have something in mind?

Well, you basically implemented a get-CONSTRUCTOR-elt-at-offset
which is useful in other parts of the compiler.  So I don't see why
it shouldn't work for general flex-arrays.

> >
> > ?  And why not use simply
> >
> >   field_size = TYPE_SIZE (TREE_TYPE (cval));
> >
> > like you do in c_strlen?
>
> Yes, that's simpler, thanks.
>
> >
> > Otherwise looks reasonable.
>
> Attached is an updated patch.  I also enhanced the handling
> of non-constant indices.  They were already handled before
> to a smaller extent.  (There may be other opportunities
> here.)

Please don't do functional changes to a patch in review, without
exactly pointing out the change.  It makes review inefficent for me.

Looks like it might be the NULL type argument handling?

+
+      if (!field_size && TREE_CODE (cval) == STRING_CST)
+       {
+         /* Determine the size of the flexible array member from
+            the size of the string initializer provided for it.  */
+         /* unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval); */
+         /* tree eltype = TREE_TYPE (TREE_TYPE (cval)); */
+         /* len *= tree_to_uhwi (TYPE_SIZE (eltype)); */
+         /* field_size = build_int_cst (size_type_node, len); */
+         field_size = TYPE_SIZE (TREE_TYPE (cval));

why all the commented code?

OK with the comments removed and TREE_CODE (cval) == CONSTRUCTOR
handled at that point.

Thanks,
Richard.

>
> Martin
Martin Sebor July 9, 2018, 9:44 p.m. UTC | #4
On 07/09/2018 06:40 AM, Richard Biener wrote:
> On Sun, Jul 8, 2018 at 4:56 AM Martin Sebor <msebor@gmail.com> wrote:
>>
>> On 07/06/2018 09:52 AM, Richard Biener wrote:
>>> On Fri, Jul 6, 2018 at 1:54 AM Martin Sebor <msebor@gmail.com> wrote:
>>>>
>>>> GCC folds accesses to members of constant aggregates except
>>>> for character arrays/strings.  For example, the strlen() call
>>>> below is not folded:
>>>>
>>>>    const char a[][4] = { "1", "12" };
>>>>
>>>>    int f (void) { retturn strlen (a[1]); }
>>>>
>>>> The attached change set enhances the string_constant() function
>>>> to make it possible to extract string constants from aggregate
>>>> initializers (CONSTRUCTORS).
>>>>
>>>> The initial solution was much simpler but as is often the case,
>>>> MEM_REF made it fail to fold things like:
>>>>
>>>>    int f (void) { retturn strlen (a[1] + 1); }
>>>>
>>>> Handling those made the project a bit more interesting and
>>>> the final solution somewhat more involved.
>>>>
>>>> To handle offsets into aggregate string members the patch also
>>>> extends the fold_ctor_reference() function to extract entire
>>>> string array initializers even if the offset points past
>>>> the beginning of the string and even though the size and
>>>> exact type of the reference are not known (there isn't enough
>>>> information in a MEM_REF to determine that).
>>>>
>>>> Tested along with the patch for PR 86415 on x86_64-linux.
>>>
>>> +      if (TREE_CODE (init) == CONSTRUCTOR)
>>> +       {
>>> +         tree type;
>>> +         if (TREE_CODE (arg) == ARRAY_REF
>>> +             || TREE_CODE (arg) == MEM_REF)
>>> +           type = TREE_TYPE (arg);
>>> +         else if (TREE_CODE (arg) == COMPONENT_REF)
>>> +           {
>>> +             tree field = TREE_OPERAND (arg, 1);
>>> +             type = TREE_TYPE (field);
>>> +           }
>>> +         else
>>> +           return NULL_TREE;
>>>
>>> what's wrong with just
>>>
>>>     type = TREE_TYPE (field);
>>
>> In response to your comment below abut size I simplified things
>> further so determining the type a priori is no longer necessary.
>>
>>> ?
>>>
>>> +         base_off *= BITS_PER_UNIT;
>>>
>>> poly_uint64 isn't enough for "bits", with wide-int you'd use offset_int,
>>> for poly you'd then use poly_offset?
>>
>> Okay, I tried to avoid the overflow.  (Converting between all
>> these flavors of wide int types is a monumental PITA.)
>>
>>>
>>> You extend fold_ctor_reference to treat size == 0 specially but then
>>> bother to compute a size here - that looks unneeded?
>>
>> Yes, well spotted, thanks!  I simplified the code so this isn't
>> necessary, and neither is the type.
>>
>>>
>>> While the offset of the reference determines the first field in the
>>> CONSTRUCTOR, how do you know the access doesn't touch
>>> adjacent ones?  STRING_CSTs do not have to be '\0' terminated,
>>> so consider
>>>
>>>   char x[2][4] = { "abcd", "abcd" };
>>>
>>> and MEM[&x] with a char[8] type?  memcpy "inlining" will create
>>> such MEMs for example.
>>
>> The code is only used to find string constants in initializer
>> expressions where I don't think the size of the access comes
>> into play.  If a memcpy() call results in a MEM_REF[char[8],
>> &x, 8] that's fine.  It's a valid reference and we can still
>> get the underlying character sequence (which is represented
>> as two STRING_CSTs with the two string literals).  I might
>> be missing the point of your question.
>
> Maybe irrelevant for strlen folding depending on what you do
> for missing '\0' termination.
>
>>>
>>> @@ -6554,8 +6577,16 @@ fold_nonarray_ctor_reference (tree type, tree ctor,
>>>        tree byte_offset = DECL_FIELD_OFFSET (cfield);
>>>        tree field_offset = DECL_FIELD_BIT_OFFSET (cfield);
>>>        tree field_size = DECL_SIZE (cfield);
>>> -      offset_int bitoffset;
>>> -      offset_int bitoffset_end, access_end;
>>> +
>>> +      if (!field_size && TREE_CODE (cval) == STRING_CST)
>>> +       {
>>> +         /* Determine the size of the flexible array member from
>>> +            the size of the string initializer provided for it.  */
>>> +         unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval);
>>> +         tree eltype = TREE_TYPE (TREE_TYPE (cval));
>>> +         len *= tree_to_uhwi (TYPE_SIZE (eltype));
>>> +         field_size = build_int_cst (size_type_node, len);
>>> +       }
>>>
>>> Why does this only apply to STRING_CST initializers and not CONSTRUCTORS,
>>> say, for
>>>
>>> struct S { int i; int a[]; } s = { 1, { 2, 3, 4, 5, 6 } };
>>
>> I can't think of a use for it.  Do you have something in mind?
>
> Well, you basically implemented a get-CONSTRUCTOR-elt-at-offset
> which is useful in other parts of the compiler.  So I don't see why
> it shouldn't work for general flex-arrays.
>
>>>
>>> ?  And why not use simply
>>>
>>>   field_size = TYPE_SIZE (TREE_TYPE (cval));
>>>
>>> like you do in c_strlen?
>>
>> Yes, that's simpler, thanks.
>>
>>>
>>> Otherwise looks reasonable.
>>
>> Attached is an updated patch.  I also enhanced the handling
>> of non-constant indices.  They were already handled before
>> to a smaller extent.  (There may be other opportunities
>> here.)
>
> Please don't do functional changes to a patch in review, without
> exactly pointing out the change.  It makes review inefficent for me.
>
> Looks like it might be the NULL type argument handling?

Sorry.  The change I was referring to is the addition and handling
of the varidx variable to string_constant.  It was necessary for
parity with the existing optimization for non-constant array
indices.  It makes it possible to fold strlen calls like:

   const char a[][3] = { "", "1", "12" };

   void f (int i)
   {
     if (__builtin_strlen (&a[2][i]) > 2)
       __builtin_abort ();
   }

Prior to the patch GCC could that for one-dimensional arrays
so to my mind it would have been an oversight for a patch to
handle multi-dimensional arrays not to do the same thing for
those.

> +
> +      if (!field_size && TREE_CODE (cval) == STRING_CST)
> +       {
> +         /* Determine the size of the flexible array member from
> +            the size of the string initializer provided for it.  */
> +         /* unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval); */
> +         /* tree eltype = TREE_TYPE (TREE_TYPE (cval)); */
> +         /* len *= tree_to_uhwi (TYPE_SIZE (eltype)); */
> +         /* field_size = build_int_cst (size_type_node, len); */
> +         field_size = TYPE_SIZE (TREE_TYPE (cval));
>
> why all the commented code?
>
> OK with the comments removed and TREE_CODE (cval) == CONSTRUCTOR
> handled at that point.

Done in r262522.

Thanks.
Martin
Andre Vieira (lists) July 11, 2018, 10 a.m. UTC | #5
On 09/07/18 22:44, Martin Sebor wrote:
> On 07/09/2018 06:40 AM, Richard Biener wrote:
>> On Sun, Jul 8, 2018 at 4:56 AM Martin Sebor <msebor@gmail.com> wrote:
>>>
>>> On 07/06/2018 09:52 AM, Richard Biener wrote:
>>>> On Fri, Jul 6, 2018 at 1:54 AM Martin Sebor <msebor@gmail.com> wrote:
>>>>>
>>>>> GCC folds accesses to members of constant aggregates except
>>>>> for character arrays/strings.  For example, the strlen() call
>>>>> below is not folded:
>>>>>
>>>>>    const char a[][4] = { "1", "12" };
>>>>>
>>>>>    int f (void) { retturn strlen (a[1]); }
>>>>>
>>>>> The attached change set enhances the string_constant() function
>>>>> to make it possible to extract string constants from aggregate
>>>>> initializers (CONSTRUCTORS).
>>>>>
>>>>> The initial solution was much simpler but as is often the case,
>>>>> MEM_REF made it fail to fold things like:
>>>>>
>>>>>    int f (void) { retturn strlen (a[1] + 1); }
>>>>>
>>>>> Handling those made the project a bit more interesting and
>>>>> the final solution somewhat more involved.
>>>>>
>>>>> To handle offsets into aggregate string members the patch also
>>>>> extends the fold_ctor_reference() function to extract entire
>>>>> string array initializers even if the offset points past
>>>>> the beginning of the string and even though the size and
>>>>> exact type of the reference are not known (there isn't enough
>>>>> information in a MEM_REF to determine that).
>>>>>
>>>>> Tested along with the patch for PR 86415 on x86_64-linux.
>>>>
>>>> +      if (TREE_CODE (init) == CONSTRUCTOR)
>>>> +       {
>>>> +         tree type;
>>>> +         if (TREE_CODE (arg) == ARRAY_REF
>>>> +             || TREE_CODE (arg) == MEM_REF)
>>>> +           type = TREE_TYPE (arg);
>>>> +         else if (TREE_CODE (arg) == COMPONENT_REF)
>>>> +           {
>>>> +             tree field = TREE_OPERAND (arg, 1);
>>>> +             type = TREE_TYPE (field);
>>>> +           }
>>>> +         else
>>>> +           return NULL_TREE;
>>>>
>>>> what's wrong with just
>>>>
>>>>     type = TREE_TYPE (field);
>>>
>>> In response to your comment below abut size I simplified things
>>> further so determining the type a priori is no longer necessary.
>>>
>>>> ?
>>>>
>>>> +         base_off *= BITS_PER_UNIT;
>>>>
>>>> poly_uint64 isn't enough for "bits", with wide-int you'd use
>>>> offset_int,
>>>> for poly you'd then use poly_offset?
>>>
>>> Okay, I tried to avoid the overflow.  (Converting between all
>>> these flavors of wide int types is a monumental PITA.)
>>>
>>>>
>>>> You extend fold_ctor_reference to treat size == 0 specially but then
>>>> bother to compute a size here - that looks unneeded?
>>>
>>> Yes, well spotted, thanks!  I simplified the code so this isn't
>>> necessary, and neither is the type.
>>>
>>>>
>>>> While the offset of the reference determines the first field in the
>>>> CONSTRUCTOR, how do you know the access doesn't touch
>>>> adjacent ones?  STRING_CSTs do not have to be '\0' terminated,
>>>> so consider
>>>>
>>>>   char x[2][4] = { "abcd", "abcd" };
>>>>
>>>> and MEM[&x] with a char[8] type?  memcpy "inlining" will create
>>>> such MEMs for example.
>>>
>>> The code is only used to find string constants in initializer
>>> expressions where I don't think the size of the access comes
>>> into play.  If a memcpy() call results in a MEM_REF[char[8],
>>> &x, 8] that's fine.  It's a valid reference and we can still
>>> get the underlying character sequence (which is represented
>>> as two STRING_CSTs with the two string literals).  I might
>>> be missing the point of your question.
>>
>> Maybe irrelevant for strlen folding depending on what you do
>> for missing '\0' termination.
>>
>>>>
>>>> @@ -6554,8 +6577,16 @@ fold_nonarray_ctor_reference (tree type, tree
>>>> ctor,
>>>>        tree byte_offset = DECL_FIELD_OFFSET (cfield);
>>>>        tree field_offset = DECL_FIELD_BIT_OFFSET (cfield);
>>>>        tree field_size = DECL_SIZE (cfield);
>>>> -      offset_int bitoffset;
>>>> -      offset_int bitoffset_end, access_end;
>>>> +
>>>> +      if (!field_size && TREE_CODE (cval) == STRING_CST)
>>>> +       {
>>>> +         /* Determine the size of the flexible array member from
>>>> +            the size of the string initializer provided for it.  */
>>>> +         unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval);
>>>> +         tree eltype = TREE_TYPE (TREE_TYPE (cval));
>>>> +         len *= tree_to_uhwi (TYPE_SIZE (eltype));
>>>> +         field_size = build_int_cst (size_type_node, len);
>>>> +       }
>>>>
>>>> Why does this only apply to STRING_CST initializers and not
>>>> CONSTRUCTORS,
>>>> say, for
>>>>
>>>> struct S { int i; int a[]; } s = { 1, { 2, 3, 4, 5, 6 } };
>>>
>>> I can't think of a use for it.  Do you have something in mind?
>>
>> Well, you basically implemented a get-CONSTRUCTOR-elt-at-offset
>> which is useful in other parts of the compiler.  So I don't see why
>> it shouldn't work for general flex-arrays.
>>
>>>>
>>>> ?  And why not use simply
>>>>
>>>>   field_size = TYPE_SIZE (TREE_TYPE (cval));
>>>>
>>>> like you do in c_strlen?
>>>
>>> Yes, that's simpler, thanks.
>>>
>>>>
>>>> Otherwise looks reasonable.
>>>
>>> Attached is an updated patch.  I also enhanced the handling
>>> of non-constant indices.  They were already handled before
>>> to a smaller extent.  (There may be other opportunities
>>> here.)
>>
>> Please don't do functional changes to a patch in review, without
>> exactly pointing out the change.  It makes review inefficent for me.
>>
>> Looks like it might be the NULL type argument handling?
> 
> Sorry.  The change I was referring to is the addition and handling
> of the varidx variable to string_constant.  It was necessary for
> parity with the existing optimization for non-constant array
> indices.  It makes it possible to fold strlen calls like:
> 
>   const char a[][3] = { "", "1", "12" };
> 
>   void f (int i)
>   {
>     if (__builtin_strlen (&a[2][i]) > 2)
>       __builtin_abort ();
>   }
> 
> Prior to the patch GCC could that for one-dimensional arrays
> so to my mind it would have been an oversight for a patch to
> handle multi-dimensional arrays not to do the same thing for
> those.
> 
>> +
>> +      if (!field_size && TREE_CODE (cval) == STRING_CST)
>> +       {
>> +         /* Determine the size of the flexible array member from
>> +            the size of the string initializer provided for it.  */
>> +         /* unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval); */
>> +         /* tree eltype = TREE_TYPE (TREE_TYPE (cval)); */
>> +         /* len *= tree_to_uhwi (TYPE_SIZE (eltype)); */
>> +         /* field_size = build_int_cst (size_type_node, len); */
>> +         field_size = TYPE_SIZE (TREE_TYPE (cval));
>>
>> why all the commented code?
>>
>> OK with the comments removed and TREE_CODE (cval) == CONSTRUCTOR
>> handled at that point.
> 
> Done in r262522.
> 
> Thanks.
> Martin
Hi Martin,

I believe this caused the following regresion on aarch64 and arm:
FAIL: gcc.c-torture/execute/builtins/strlen-3.c execution, -Og -g

I haven't quite looked into why though, Ill have a closer look.

Cheers,
Andre
Andre Vieira (lists) July 11, 2018, 1:50 p.m. UTC | #6
On 11/07/18 11:00, Andre Vieira (lists) wrote:
> On 09/07/18 22:44, Martin Sebor wrote:
>> On 07/09/2018 06:40 AM, Richard Biener wrote:
>>> On Sun, Jul 8, 2018 at 4:56 AM Martin Sebor <msebor@gmail.com> wrote:
>>>>
>>>> On 07/06/2018 09:52 AM, Richard Biener wrote:
>>>>> On Fri, Jul 6, 2018 at 1:54 AM Martin Sebor <msebor@gmail.com> wrote:
>>>>>>
>>>>>> GCC folds accesses to members of constant aggregates except
>>>>>> for character arrays/strings.  For example, the strlen() call
>>>>>> below is not folded:
>>>>>>
>>>>>>    const char a[][4] = { "1", "12" };
>>>>>>
>>>>>>    int f (void) { retturn strlen (a[1]); }
>>>>>>
>>>>>> The attached change set enhances the string_constant() function
>>>>>> to make it possible to extract string constants from aggregate
>>>>>> initializers (CONSTRUCTORS).
>>>>>>
>>>>>> The initial solution was much simpler but as is often the case,
>>>>>> MEM_REF made it fail to fold things like:
>>>>>>
>>>>>>    int f (void) { retturn strlen (a[1] + 1); }
>>>>>>
>>>>>> Handling those made the project a bit more interesting and
>>>>>> the final solution somewhat more involved.
>>>>>>
>>>>>> To handle offsets into aggregate string members the patch also
>>>>>> extends the fold_ctor_reference() function to extract entire
>>>>>> string array initializers even if the offset points past
>>>>>> the beginning of the string and even though the size and
>>>>>> exact type of the reference are not known (there isn't enough
>>>>>> information in a MEM_REF to determine that).
>>>>>>
>>>>>> Tested along with the patch for PR 86415 on x86_64-linux.
>>>>>
>>>>> +      if (TREE_CODE (init) == CONSTRUCTOR)
>>>>> +       {
>>>>> +         tree type;
>>>>> +         if (TREE_CODE (arg) == ARRAY_REF
>>>>> +             || TREE_CODE (arg) == MEM_REF)
>>>>> +           type = TREE_TYPE (arg);
>>>>> +         else if (TREE_CODE (arg) == COMPONENT_REF)
>>>>> +           {
>>>>> +             tree field = TREE_OPERAND (arg, 1);
>>>>> +             type = TREE_TYPE (field);
>>>>> +           }
>>>>> +         else
>>>>> +           return NULL_TREE;
>>>>>
>>>>> what's wrong with just
>>>>>
>>>>>     type = TREE_TYPE (field);
>>>>
>>>> In response to your comment below abut size I simplified things
>>>> further so determining the type a priori is no longer necessary.
>>>>
>>>>> ?
>>>>>
>>>>> +         base_off *= BITS_PER_UNIT;
>>>>>
>>>>> poly_uint64 isn't enough for "bits", with wide-int you'd use
>>>>> offset_int,
>>>>> for poly you'd then use poly_offset?
>>>>
>>>> Okay, I tried to avoid the overflow.  (Converting between all
>>>> these flavors of wide int types is a monumental PITA.)
>>>>
>>>>>
>>>>> You extend fold_ctor_reference to treat size == 0 specially but then
>>>>> bother to compute a size here - that looks unneeded?
>>>>
>>>> Yes, well spotted, thanks!  I simplified the code so this isn't
>>>> necessary, and neither is the type.
>>>>
>>>>>
>>>>> While the offset of the reference determines the first field in the
>>>>> CONSTRUCTOR, how do you know the access doesn't touch
>>>>> adjacent ones?  STRING_CSTs do not have to be '\0' terminated,
>>>>> so consider
>>>>>
>>>>>   char x[2][4] = { "abcd", "abcd" };
>>>>>
>>>>> and MEM[&x] with a char[8] type?  memcpy "inlining" will create
>>>>> such MEMs for example.
>>>>
>>>> The code is only used to find string constants in initializer
>>>> expressions where I don't think the size of the access comes
>>>> into play.  If a memcpy() call results in a MEM_REF[char[8],
>>>> &x, 8] that's fine.  It's a valid reference and we can still
>>>> get the underlying character sequence (which is represented
>>>> as two STRING_CSTs with the two string literals).  I might
>>>> be missing the point of your question.
>>>
>>> Maybe irrelevant for strlen folding depending on what you do
>>> for missing '\0' termination.
>>>
>>>>>
>>>>> @@ -6554,8 +6577,16 @@ fold_nonarray_ctor_reference (tree type, tree
>>>>> ctor,
>>>>>        tree byte_offset = DECL_FIELD_OFFSET (cfield);
>>>>>        tree field_offset = DECL_FIELD_BIT_OFFSET (cfield);
>>>>>        tree field_size = DECL_SIZE (cfield);
>>>>> -      offset_int bitoffset;
>>>>> -      offset_int bitoffset_end, access_end;
>>>>> +
>>>>> +      if (!field_size && TREE_CODE (cval) == STRING_CST)
>>>>> +       {
>>>>> +         /* Determine the size of the flexible array member from
>>>>> +            the size of the string initializer provided for it.  */
>>>>> +         unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval);
>>>>> +         tree eltype = TREE_TYPE (TREE_TYPE (cval));
>>>>> +         len *= tree_to_uhwi (TYPE_SIZE (eltype));
>>>>> +         field_size = build_int_cst (size_type_node, len);
>>>>> +       }
>>>>>
>>>>> Why does this only apply to STRING_CST initializers and not
>>>>> CONSTRUCTORS,
>>>>> say, for
>>>>>
>>>>> struct S { int i; int a[]; } s = { 1, { 2, 3, 4, 5, 6 } };
>>>>
>>>> I can't think of a use for it.  Do you have something in mind?
>>>
>>> Well, you basically implemented a get-CONSTRUCTOR-elt-at-offset
>>> which is useful in other parts of the compiler.  So I don't see why
>>> it shouldn't work for general flex-arrays.
>>>
>>>>>
>>>>> ?  And why not use simply
>>>>>
>>>>>   field_size = TYPE_SIZE (TREE_TYPE (cval));
>>>>>
>>>>> like you do in c_strlen?
>>>>
>>>> Yes, that's simpler, thanks.
>>>>
>>>>>
>>>>> Otherwise looks reasonable.
>>>>
>>>> Attached is an updated patch.  I also enhanced the handling
>>>> of non-constant indices.  They were already handled before
>>>> to a smaller extent.  (There may be other opportunities
>>>> here.)
>>>
>>> Please don't do functional changes to a patch in review, without
>>> exactly pointing out the change.  It makes review inefficent for me.
>>>
>>> Looks like it might be the NULL type argument handling?
>>
>> Sorry.  The change I was referring to is the addition and handling
>> of the varidx variable to string_constant.  It was necessary for
>> parity with the existing optimization for non-constant array
>> indices.  It makes it possible to fold strlen calls like:
>>
>>   const char a[][3] = { "", "1", "12" };
>>
>>   void f (int i)
>>   {
>>     if (__builtin_strlen (&a[2][i]) > 2)
>>       __builtin_abort ();
>>   }
>>
>> Prior to the patch GCC could that for one-dimensional arrays
>> so to my mind it would have been an oversight for a patch to
>> handle multi-dimensional arrays not to do the same thing for
>> those.
>>
>>> +
>>> +      if (!field_size && TREE_CODE (cval) == STRING_CST)
>>> +       {
>>> +         /* Determine the size of the flexible array member from
>>> +            the size of the string initializer provided for it.  */
>>> +         /* unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval); */
>>> +         /* tree eltype = TREE_TYPE (TREE_TYPE (cval)); */
>>> +         /* len *= tree_to_uhwi (TYPE_SIZE (eltype)); */
>>> +         /* field_size = build_int_cst (size_type_node, len); */
>>> +         field_size = TYPE_SIZE (TREE_TYPE (cval));
>>>
>>> why all the commented code?
>>>
>>> OK with the comments removed and TREE_CODE (cval) == CONSTRUCTOR
>>> handled at that point.
>>
>> Done in r262522.
>>
>> Thanks.
>> Martin
> Hi Martin,
> 
> I believe this caused the following regresion on aarch64 and arm:
> FAIL: gcc.c-torture/execute/builtins/strlen-3.c execution, -Og -g
> 
> I haven't quite looked into why though, Ill have a closer look.
> 
> Cheers,
> Andre
> 
Hi,

After having a look it seems the strlen call:
 'strlen (larger + (x++ & 7))'
is not optmized away for with -Og, which means it will call the strlen
provided and because you removed the "inside_main" setting around the
strlen will abort as __OPTIMIZE__ is set with -Og.  Maybe its worth
skipping this test for -Og?

Cheers,
Andre
Martin Sebor July 11, 2018, 2:02 p.m. UTC | #7
On 07/11/2018 07:50 AM, Andre Vieira (lists) wrote:
> On 11/07/18 11:00, Andre Vieira (lists) wrote:
>> On 09/07/18 22:44, Martin Sebor wrote:
>>> On 07/09/2018 06:40 AM, Richard Biener wrote:
>>>> On Sun, Jul 8, 2018 at 4:56 AM Martin Sebor <msebor@gmail.com> wrote:
>>>>>
>>>>> On 07/06/2018 09:52 AM, Richard Biener wrote:
>>>>>> On Fri, Jul 6, 2018 at 1:54 AM Martin Sebor <msebor@gmail.com> wrote:
>>>>>>>
>>>>>>> GCC folds accesses to members of constant aggregates except
>>>>>>> for character arrays/strings.  For example, the strlen() call
>>>>>>> below is not folded:
>>>>>>>
>>>>>>>    const char a[][4] = { "1", "12" };
>>>>>>>
>>>>>>>    int f (void) { retturn strlen (a[1]); }
>>>>>>>
>>>>>>> The attached change set enhances the string_constant() function
>>>>>>> to make it possible to extract string constants from aggregate
>>>>>>> initializers (CONSTRUCTORS).
>>>>>>>
>>>>>>> The initial solution was much simpler but as is often the case,
>>>>>>> MEM_REF made it fail to fold things like:
>>>>>>>
>>>>>>>    int f (void) { retturn strlen (a[1] + 1); }
>>>>>>>
>>>>>>> Handling those made the project a bit more interesting and
>>>>>>> the final solution somewhat more involved.
>>>>>>>
>>>>>>> To handle offsets into aggregate string members the patch also
>>>>>>> extends the fold_ctor_reference() function to extract entire
>>>>>>> string array initializers even if the offset points past
>>>>>>> the beginning of the string and even though the size and
>>>>>>> exact type of the reference are not known (there isn't enough
>>>>>>> information in a MEM_REF to determine that).
>>>>>>>
>>>>>>> Tested along with the patch for PR 86415 on x86_64-linux.
>>>>>>
>>>>>> +      if (TREE_CODE (init) == CONSTRUCTOR)
>>>>>> +       {
>>>>>> +         tree type;
>>>>>> +         if (TREE_CODE (arg) == ARRAY_REF
>>>>>> +             || TREE_CODE (arg) == MEM_REF)
>>>>>> +           type = TREE_TYPE (arg);
>>>>>> +         else if (TREE_CODE (arg) == COMPONENT_REF)
>>>>>> +           {
>>>>>> +             tree field = TREE_OPERAND (arg, 1);
>>>>>> +             type = TREE_TYPE (field);
>>>>>> +           }
>>>>>> +         else
>>>>>> +           return NULL_TREE;
>>>>>>
>>>>>> what's wrong with just
>>>>>>
>>>>>>     type = TREE_TYPE (field);
>>>>>
>>>>> In response to your comment below abut size I simplified things
>>>>> further so determining the type a priori is no longer necessary.
>>>>>
>>>>>> ?
>>>>>>
>>>>>> +         base_off *= BITS_PER_UNIT;
>>>>>>
>>>>>> poly_uint64 isn't enough for "bits", with wide-int you'd use
>>>>>> offset_int,
>>>>>> for poly you'd then use poly_offset?
>>>>>
>>>>> Okay, I tried to avoid the overflow.  (Converting between all
>>>>> these flavors of wide int types is a monumental PITA.)
>>>>>
>>>>>>
>>>>>> You extend fold_ctor_reference to treat size == 0 specially but then
>>>>>> bother to compute a size here - that looks unneeded?
>>>>>
>>>>> Yes, well spotted, thanks!  I simplified the code so this isn't
>>>>> necessary, and neither is the type.
>>>>>
>>>>>>
>>>>>> While the offset of the reference determines the first field in the
>>>>>> CONSTRUCTOR, how do you know the access doesn't touch
>>>>>> adjacent ones?  STRING_CSTs do not have to be '\0' terminated,
>>>>>> so consider
>>>>>>
>>>>>>   char x[2][4] = { "abcd", "abcd" };
>>>>>>
>>>>>> and MEM[&x] with a char[8] type?  memcpy "inlining" will create
>>>>>> such MEMs for example.
>>>>>
>>>>> The code is only used to find string constants in initializer
>>>>> expressions where I don't think the size of the access comes
>>>>> into play.  If a memcpy() call results in a MEM_REF[char[8],
>>>>> &x, 8] that's fine.  It's a valid reference and we can still
>>>>> get the underlying character sequence (which is represented
>>>>> as two STRING_CSTs with the two string literals).  I might
>>>>> be missing the point of your question.
>>>>
>>>> Maybe irrelevant for strlen folding depending on what you do
>>>> for missing '\0' termination.
>>>>
>>>>>>
>>>>>> @@ -6554,8 +6577,16 @@ fold_nonarray_ctor_reference (tree type, tree
>>>>>> ctor,
>>>>>>        tree byte_offset = DECL_FIELD_OFFSET (cfield);
>>>>>>        tree field_offset = DECL_FIELD_BIT_OFFSET (cfield);
>>>>>>        tree field_size = DECL_SIZE (cfield);
>>>>>> -      offset_int bitoffset;
>>>>>> -      offset_int bitoffset_end, access_end;
>>>>>> +
>>>>>> +      if (!field_size && TREE_CODE (cval) == STRING_CST)
>>>>>> +       {
>>>>>> +         /* Determine the size of the flexible array member from
>>>>>> +            the size of the string initializer provided for it.  */
>>>>>> +         unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval);
>>>>>> +         tree eltype = TREE_TYPE (TREE_TYPE (cval));
>>>>>> +         len *= tree_to_uhwi (TYPE_SIZE (eltype));
>>>>>> +         field_size = build_int_cst (size_type_node, len);
>>>>>> +       }
>>>>>>
>>>>>> Why does this only apply to STRING_CST initializers and not
>>>>>> CONSTRUCTORS,
>>>>>> say, for
>>>>>>
>>>>>> struct S { int i; int a[]; } s = { 1, { 2, 3, 4, 5, 6 } };
>>>>>
>>>>> I can't think of a use for it.  Do you have something in mind?
>>>>
>>>> Well, you basically implemented a get-CONSTRUCTOR-elt-at-offset
>>>> which is useful in other parts of the compiler.  So I don't see why
>>>> it shouldn't work for general flex-arrays.
>>>>
>>>>>>
>>>>>> ?  And why not use simply
>>>>>>
>>>>>>   field_size = TYPE_SIZE (TREE_TYPE (cval));
>>>>>>
>>>>>> like you do in c_strlen?
>>>>>
>>>>> Yes, that's simpler, thanks.
>>>>>
>>>>>>
>>>>>> Otherwise looks reasonable.
>>>>>
>>>>> Attached is an updated patch.  I also enhanced the handling
>>>>> of non-constant indices.  They were already handled before
>>>>> to a smaller extent.  (There may be other opportunities
>>>>> here.)
>>>>
>>>> Please don't do functional changes to a patch in review, without
>>>> exactly pointing out the change.  It makes review inefficent for me.
>>>>
>>>> Looks like it might be the NULL type argument handling?
>>>
>>> Sorry.  The change I was referring to is the addition and handling
>>> of the varidx variable to string_constant.  It was necessary for
>>> parity with the existing optimization for non-constant array
>>> indices.  It makes it possible to fold strlen calls like:
>>>
>>>   const char a[][3] = { "", "1", "12" };
>>>
>>>   void f (int i)
>>>   {
>>>     if (__builtin_strlen (&a[2][i]) > 2)
>>>       __builtin_abort ();
>>>   }
>>>
>>> Prior to the patch GCC could that for one-dimensional arrays
>>> so to my mind it would have been an oversight for a patch to
>>> handle multi-dimensional arrays not to do the same thing for
>>> those.
>>>
>>>> +
>>>> +      if (!field_size && TREE_CODE (cval) == STRING_CST)
>>>> +       {
>>>> +         /* Determine the size of the flexible array member from
>>>> +            the size of the string initializer provided for it.  */
>>>> +         /* unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval); */
>>>> +         /* tree eltype = TREE_TYPE (TREE_TYPE (cval)); */
>>>> +         /* len *= tree_to_uhwi (TYPE_SIZE (eltype)); */
>>>> +         /* field_size = build_int_cst (size_type_node, len); */
>>>> +         field_size = TYPE_SIZE (TREE_TYPE (cval));
>>>>
>>>> why all the commented code?
>>>>
>>>> OK with the comments removed and TREE_CODE (cval) == CONSTRUCTOR
>>>> handled at that point.
>>>
>>> Done in r262522.
>>>
>>> Thanks.
>>> Martin
>> Hi Martin,
>>
>> I believe this caused the following regresion on aarch64 and arm:
>> FAIL: gcc.c-torture/execute/builtins/strlen-3.c execution, -Og -g
>>
>> I haven't quite looked into why though, Ill have a closer look.
>>
>> Cheers,
>> Andre
>>
> Hi,
>
> After having a look it seems the strlen call:
>  'strlen (larger + (x++ & 7))'
> is not optmized away for with -Og, which means it will call the strlen
> provided and because you removed the "inside_main" setting around the
> strlen will abort as __OPTIMIZE__ is set with -Og.  Maybe its worth
> skipping this test for -Og?

Yes.  It wasn't necessary to tighten up the test (in fact it
was a mistake).  I reverted the change yesterday in response
to reports like yours (pr86461).  I need to find a different
way to exercise the part of the change.

Sorry about that!

Martin
diff mbox series

Patch

PR middle-end/77357 - strlen of constant strings not folded

gcc/ChangeLog:

	* builtins.c (c_strlen): Avoid out-of-bounds warnings when
	accessing implicitly initialized array elements.
	* expr.c (string_constant): Handle string initializers of
	character arrays within aggregates.
	* gimple-fold.c (fold_array_ctor_reference): Add argument.
	Store element offset.  As a special case, handle zero size.
	(fold_nonarray_ctor_reference): Same.
	(fold_ctor_reference): Add argument.  Store subobject offset.
	* gimple-fold.h (fold_ctor_reference): Add argument.

gcc/testsuite/ChangeLog:

	PR middle-end/77357
	* gcc.dg/strlenopt-49.c: New test.
	* gcc.dg/strlenopt-50.c: New test.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 91658e8..2f9d5d7 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -602,8 +602,15 @@  c_strlen (tree src, int only_value)
     = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (src))));
 
   /* Set MAXELTS to sizeof (SRC) / sizeof (*SRC) - 1, the maximum possible
-     length of SRC.  */
-  unsigned maxelts = TREE_STRING_LENGTH (src) / eltsize - 1;
+     length of SRC.  Prefer TYPE_SIZE() to TREE_STRING_LENGTH() if possible
+     in case the latter is less than the size of the array.  */
+  HOST_WIDE_INT maxelts = TREE_STRING_LENGTH (src);
+  tree type = TREE_TYPE (src);
+  if (tree size = TYPE_SIZE_UNIT (type))
+    if (tree_fits_shwi_p (size))
+      maxelts = tree_to_uhwi (size);
+
+  maxelts = maxelts / eltsize - 1;
 
   /* PTR can point to the byte representation of any string type, including
      char* and wchar_t*.  */
diff --git a/gcc/expr.c b/gcc/expr.c
index 56751df..be3ab93 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -54,11 +54,13 @@  along with GCC; see the file COPYING3.  If not see
 #include "reload.h"
 #include "langhooks.h"
 #include "common/common-target.h"
+#include "tree-dfa.h"
 #include "tree-ssa-live.h"
 #include "tree-outof-ssa.h"
 #include "tree-ssa-address.h"
 #include "builtins.h"
 #include "ccmp.h"
+#include "gimple-fold.h"
 #include "rtx-vector-builder.h"
 
 
@@ -11274,54 +11276,20 @@  is_aligning_offset (const_tree offset, const_tree exp)
 tree
 string_constant (tree arg, tree *ptr_offset)
 {
-  tree array, offset, lower_bound;
+  tree array;
   STRIP_NOPS (arg);
 
+  poly_int64 base_off = 0;
+
   if (TREE_CODE (arg) == ADDR_EXPR)
     {
-      if (TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST)
-	{
-	  *ptr_offset = size_zero_node;
-	  return TREE_OPERAND (arg, 0);
-	}
-      else if (TREE_CODE (TREE_OPERAND (arg, 0)) == VAR_DECL)
-	{
-	  array = TREE_OPERAND (arg, 0);
-	  offset = size_zero_node;
-	}
-      else if (TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
-	{
-	  array = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
-	  offset = TREE_OPERAND (TREE_OPERAND (arg, 0), 1);
-	  if (TREE_CODE (array) != STRING_CST && !VAR_P (array))
-	    return 0;
-
-	  /* Check if the array has a nonzero lower bound.  */
-	  lower_bound = array_ref_low_bound (TREE_OPERAND (arg, 0));
-	  if (!integer_zerop (lower_bound))
-	    {
-	      /* If the offset and base aren't both constants, return 0.  */
-	      if (TREE_CODE (lower_bound) != INTEGER_CST)
-	        return 0;
-	      if (TREE_CODE (offset) != INTEGER_CST)
-		return 0;
-	      /* Adjust offset by the lower bound.  */
-	      offset = size_diffop (fold_convert (sizetype, offset),
-				    fold_convert (sizetype, lower_bound));
-	    }
-	}
-      else if (TREE_CODE (TREE_OPERAND (arg, 0)) == MEM_REF)
-	{
-	  array = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
-	  offset = TREE_OPERAND (TREE_OPERAND (arg, 0), 1);
-	  if (TREE_CODE (array) != ADDR_EXPR)
-	    return 0;
-	  array = TREE_OPERAND (array, 0);
-	  if (TREE_CODE (array) != STRING_CST && !VAR_P (array))
-	    return 0;
-	}
-      else
-	return 0;
+      arg = TREE_OPERAND (arg, 0);
+      array = get_addr_base_and_unit_offset (arg, &base_off);
+      if (!array
+	  || (TREE_CODE (array) != VAR_DECL
+	      && TREE_CODE (array) != CONST_DECL
+	      && TREE_CODE (array) != STRING_CST))
+	return NULL_TREE;
     }
   else if (TREE_CODE (arg) == PLUS_EXPR || TREE_CODE (arg) == POINTER_PLUS_EXPR)
     {
@@ -11331,25 +11299,26 @@  string_constant (tree arg, tree *ptr_offset)
       STRIP_NOPS (arg0);
       STRIP_NOPS (arg1);
 
-      if (TREE_CODE (arg0) == ADDR_EXPR
-	  && (TREE_CODE (TREE_OPERAND (arg0, 0)) == STRING_CST
-	      || TREE_CODE (TREE_OPERAND (arg0, 0)) == VAR_DECL))
-	{
-	  array = TREE_OPERAND (arg0, 0);
-	  offset = arg1;
-	}
-      else if (TREE_CODE (arg1) == ADDR_EXPR
-	       && (TREE_CODE (TREE_OPERAND (arg1, 0)) == STRING_CST
-		   || TREE_CODE (TREE_OPERAND (arg1, 0)) == VAR_DECL))
+      if (TREE_CODE (arg0) == ADDR_EXPR)
+	;   /* Do nothing.  */
+      else if (TREE_CODE (arg1) == ADDR_EXPR)
+	std::swap (arg0, arg1);
+      else
+	return NULL_TREE;
+
+      tree offset;
+      if (tree str = string_constant (arg0, &offset))
 	{
-	  array = TREE_OPERAND (arg1, 0);
-	  offset = arg0;
+	  tree type = TREE_TYPE (arg1);
+	  *ptr_offset = fold_build2 (PLUS_EXPR, type, offset, arg1);
+	  return str;
 	}
-      else
-	return 0;
+      return NULL_TREE;
     }
   else
-    return 0;
+    return NULL_TREE;
+
+  tree offset = wide_int_to_tree (sizetype, base_off);
 
   if (TREE_CODE (array) == STRING_CST)
     {
@@ -11362,9 +11331,40 @@  string_constant (tree arg, tree *ptr_offset)
       tree init = ctor_for_folding (array);
 
       /* Variables initialized to string literals can be handled too.  */
-      if (init == error_mark_node
-	  || !init
-	  || TREE_CODE (init) != STRING_CST)
+      if (!init || init == error_mark_node)
+	return NULL_TREE;
+      if (TREE_CODE (init) == CONSTRUCTOR)
+	{
+	  tree type;
+	  if (TREE_CODE (arg) == ARRAY_REF
+	      || TREE_CODE (arg) == MEM_REF)
+	    type = TREE_TYPE (arg);
+	  else if (TREE_CODE (arg) == COMPONENT_REF)
+	    {
+	      tree field = TREE_OPERAND (arg, 1);
+	      type = TREE_TYPE (field);
+	    }
+	  else
+	    return NULL_TREE;
+
+	  tree typesize = TYPE_SIZE (type);
+	  poly_int64 size = 0;
+	  if (typesize && TREE_CODE (arg) != MEM_REF)
+	    size = tree_to_uhwi (typesize);
+
+	  base_off *= BITS_PER_UNIT;
+	  unsigned HOST_WIDE_INT fieldoff = 0;
+	  init = fold_ctor_reference (type, init, base_off, size, array,
+				      &fieldoff);
+	  HOST_WIDE_INT cstoff;
+	  if (init && base_off.is_constant (&cstoff))
+	    {
+	      cstoff = (cstoff - fieldoff) / BITS_PER_UNIT;
+	      offset = build_int_cst (sizetype, cstoff);
+	    }
+	}
+
+      if (!init || TREE_CODE (init) != STRING_CST)
 	return 0;
 
       /* Avoid const char foo[4] = "abcde";  */
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 6ce34bf..148ad43 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -6476,14 +6476,18 @@  get_base_constructor (tree base, poly_int64_pod *bit_offset,
     }
 }
 
-/* CTOR is CONSTRUCTOR of an array type.  Fold reference of type TYPE and size
-   SIZE to the memory at bit OFFSET.  */
+/* CTOR is CONSTRUCTOR of an array type.  Fold a reference of TYPE
+   and SIZE bits to the memory at bit OFFSET.  When SIZE is zero,
+   attempt to fold a reference to the entire element which OFFSET
+   refers to.  Increment *SUBOFF by the bit offset of the accessed
+   element.  */
 
 static tree
 fold_array_ctor_reference (tree type, tree ctor,
 			   unsigned HOST_WIDE_INT offset,
 			   unsigned HOST_WIDE_INT size,
-			   tree from_decl)
+			   tree from_decl,
+			   unsigned HOST_WIDE_INT *suboff)
 {
   offset_int low_bound;
   offset_int elt_size;
@@ -6529,21 +6533,40 @@  fold_array_ctor_reference (tree type, tree ctor,
   if (inner_offset + size > elt_size.to_uhwi () * BITS_PER_UNIT)
     return NULL_TREE;
   if (tree val = get_array_ctor_element_at_index (ctor, access_index))
-    return fold_ctor_reference (type, val, inner_offset, size, from_decl);
+    {
+      if (!size && TREE_CODE (val) != CONSTRUCTOR)
+	{
+	  /* For the final reference to the entire accessed element
+	     (SIZE is zero), reset INNER_OFFSET, disegard TYPE in
+	     favor of the type of the element, and set SIZE to
+	     the size of the accessed element.  */
+	  inner_offset = 0;
+	  type = TREE_TYPE (val);
+	  size = elt_size.to_uhwi () * BITS_PER_UNIT;
+	}
+
+      *suboff += (access_index * elt_size * BITS_PER_UNIT).to_uhwi ();
+      return fold_ctor_reference (type, val, inner_offset, size, from_decl,
+				  suboff);
+    }
 
-  /* When memory is not explicitely mentioned in constructor,
+  /* When memory is not explicitly mentioned in constructor,
      it is 0 (or out of range).  */
   return build_zero_cst (type);
 }
 
-/* CTOR is CONSTRUCTOR of an aggregate or vector.
-   Fold reference of type TYPE and size SIZE to the memory at bit OFFSET.  */
+/* CTOR is CONSTRUCTOR of an aggregate or vector.  Fold a reference of
+   TYPE and SIZE bits to the memory at bit OFFSET.   When SIZE is zero,
+   attempt to fold a reference to the entire member which OFFSET
+   refers to.  Increment *SUBOFF by the bit offset of the accessed
+   member.  */
 
 static tree
 fold_nonarray_ctor_reference (tree type, tree ctor,
 			      unsigned HOST_WIDE_INT offset,
 			      unsigned HOST_WIDE_INT size,
-			      tree from_decl)
+			      tree from_decl,
+			      unsigned HOST_WIDE_INT *suboff)
 {
   unsigned HOST_WIDE_INT cnt;
   tree cfield, cval;
@@ -6554,8 +6577,16 @@  fold_nonarray_ctor_reference (tree type, tree ctor,
       tree byte_offset = DECL_FIELD_OFFSET (cfield);
       tree field_offset = DECL_FIELD_BIT_OFFSET (cfield);
       tree field_size = DECL_SIZE (cfield);
-      offset_int bitoffset;
-      offset_int bitoffset_end, access_end;
+
+      if (!field_size && TREE_CODE (cval) == STRING_CST)
+	{
+	  /* Determine the size of the flexible array member from
+	     the size of the string initializer provided for it.  */
+	  unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval);
+	  tree eltype = TREE_TYPE (TREE_TYPE (cval));
+	  len *= tree_to_uhwi (TYPE_SIZE (eltype));
+	  field_size = build_int_cst (size_type_node, len);
+	}
 
       /* Variable sized objects in static constructors makes no sense,
 	 but field_size can be NULL for flexible array members.  */
@@ -6566,45 +6597,72 @@  fold_nonarray_ctor_reference (tree type, tree ctor,
 		      : TREE_CODE (TREE_TYPE (cfield)) == ARRAY_TYPE));
 
       /* Compute bit offset of the field.  */
-      bitoffset = (wi::to_offset (field_offset)
-		   + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT));
+      offset_int bitoffset
+	= (wi::to_offset (field_offset)
+	   + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT));
       /* Compute bit offset where the field ends.  */
+      offset_int bitoffset_end;
       if (field_size != NULL_TREE)
 	bitoffset_end = bitoffset + wi::to_offset (field_size);
       else
 	bitoffset_end = 0;
 
-      access_end = offset_int (offset) + size;
+      /* Compute the bit offset of the end of the desired access.
+	 As a special case, if the size of the desired access is
+	 zero, assume the access is to the entire field (and let
+	 the caller make any necessary adjustments by storing
+	 the actual bounds of the field in FIELDBOUNDS).  */
+      offset_int access_end = offset_int (offset);
+      if (size)
+	access_end += size;
+      else
+	access_end = bitoffset_end;
 
-      /* Is there any overlap between [OFFSET, OFFSET+SIZE) and
-	 [BITOFFSET, BITOFFSET_END)?  */
+      /* Is there any overlap between the desired access at
+	 [OFFSET, OFFSET+SIZE) and the offset of the field within
+	 the object at [BITOFFSET, BITOFFSET_END)?  */
       if (wi::cmps (access_end, bitoffset) > 0
 	  && (field_size == NULL_TREE
 	      || wi::lts_p (offset, bitoffset_end)))
 	{
-	  offset_int inner_offset = offset_int (offset) - bitoffset;
-	  /* We do have overlap.  Now see if field is large enough to
-	     cover the access.  Give up for accesses spanning multiple
-	     fields.  */
+	  *suboff += bitoffset.to_uhwi ();
+
+	  if (!size)
+	    {
+	      /* Set the OFFSET and SIZE to encompass the entire field.  */
+	      offset = bitoffset.to_uhwi ();
+	      size = (bitoffset_end - bitoffset).to_uhwi ();
+	    }
+
+	  /* We do have overlap.  Now see if the field is large enough
+	     to cover the access.  Give up for accesses that extend
+	     beyond the end of the object or that span multiple fields.  */
 	  if (wi::cmps (access_end, bitoffset_end) > 0)
 	    return NULL_TREE;
 	  if (offset < bitoffset)
 	    return NULL_TREE;
+
+	  offset_int inner_offset = offset_int (offset) - bitoffset;
 	  return fold_ctor_reference (type, cval,
 				      inner_offset.to_uhwi (), size,
-				      from_decl);
+				      from_decl, suboff);
 	}
     }
   /* When memory is not explicitely mentioned in constructor, it is 0.  */
   return build_zero_cst (type);
 }
 
-/* CTOR is value initializing memory, fold reference of type TYPE and
-   size POLY_SIZE to the memory at bit POLY_OFFSET.  */
+/* CTOR is value initializing memory.  Fold a reference of TYPE and
+   bit size POLY_SIZE to the memory at bit POLY_OFFSET.  When SIZE
+   is zero, attempt to fold a reference to the entire subobject
+   which OFFSET refers to.  This is used when folding accesses to
+   string members of aggregates.  When non-null, set *SUBOFF to
+   the bit offset of the accessed subobject.  */
 
 tree
-fold_ctor_reference (tree type, tree ctor, poly_uint64 poly_offset,
-		     poly_uint64 poly_size, tree from_decl)
+fold_ctor_reference (tree type, tree ctor, const poly_uint64 &poly_offset,
+		     const poly_uint64 &poly_size, tree from_decl,
+		     unsigned HOST_WIDE_INT *suboff /* = NULL */)
 {
   tree ret;
 
@@ -6650,14 +6708,17 @@  fold_ctor_reference (tree type, tree ctor, poly_uint64 poly_offset,
     }
   if (TREE_CODE (ctor) == CONSTRUCTOR)
     {
+      unsigned HOST_WIDE_INT dummy = 0;
+      if (!suboff)
+	suboff = &dummy;
 
       if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE
 	  || TREE_CODE (TREE_TYPE (ctor)) == VECTOR_TYPE)
 	return fold_array_ctor_reference (type, ctor, offset, size,
-					  from_decl);
-      else
-	return fold_nonarray_ctor_reference (type, ctor, offset, size,
-					     from_decl);
+					  from_decl, suboff);
+
+      return fold_nonarray_ctor_reference (type, ctor, offset, size,
+					   from_decl, suboff);
     }
 
   return NULL_TREE;
diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h
index 0a28ec7..04e9bfa 100644
--- a/gcc/gimple-fold.h
+++ b/gcc/gimple-fold.h
@@ -45,7 +45,9 @@  extern tree follow_all_ssa_edges (tree);
 extern tree gimple_fold_stmt_to_constant_1 (gimple *, tree (*) (tree),
 					    tree (*) (tree) = no_follow_ssa_edges);
 extern tree gimple_fold_stmt_to_constant (gimple *, tree (*) (tree));
-extern tree fold_ctor_reference (tree, tree, poly_uint64, poly_uint64, tree);
+extern tree fold_ctor_reference (tree, tree, const poly_uint64&,
+				 const poly_uint64&, tree,
+				 unsigned HOST_WIDE_INT * = NULL);
 extern tree fold_const_aggregate_ref_1 (tree, tree (*) (tree));
 extern tree fold_const_aggregate_ref (tree);
 extern tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree,
diff --git a/gcc/testsuite/gcc.dg/strlenopt-49.c b/gcc/testsuite/gcc.dg/strlenopt-49.c
new file mode 100644
index 0000000..03e063b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-49.c
@@ -0,0 +1,288 @@ 
+/* PR tree-optimization/77357 - strlen of constant strings not folded
+   { dg-do compile }
+   { dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-ccp" } */
+
+#include "strlenopt.h"
+
+#define CONCAT(x, y) x ## y
+#define CAT(x, y) CONCAT (x, y)
+#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do {				\
+    extern void FAILNAME (name) (void);		\
+    FAILNAME (name)();				\
+  } while (0)
+
+/* Macro to emit a call to funcation named
+     call_in_true_branch_not_eliminated_on_line_NNN()
+   for each call that's expected to be eliminated.  The dg-final
+   scan-tree-dump-time directive at the bottom of the test verifies
+   that no such call appears in output.  */
+#define ELIM(expr) \
+  if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+#define T(s, n) ELIM (strlen (s) == n)
+
+
+struct S
+{
+  char a1[1], a2[2], a3[3], a4[4], a5[5], a6[6], a7[7], a8[8], a9[9];
+};
+
+#define S0 ""
+#define S1 "1"
+#define S2 "12"
+#define S3 "123"
+#define S4 "1234"
+#define S5 "12345"
+#define S6 "123456"
+#define S7 "1234567"
+#define S8 "12345678"
+
+const char a9[][9] = { S0, S1, S2, S3, S4, S5, S6, S7, S8 };
+
+const char a_1_9[1][9] = { S8 };
+const char a_2_9[2][9] = { S8, S7};
+
+const char a9_9[][9][9] = {
+  { S0, S0, S0, S0, S0, S0, S0, S0, S0 },
+  { S0, S1, S1, S1, S1, S1, S1, S1, S1 },
+  { S0, S1, S2, S2, S2, S2, S2, S2, S2 },
+  { S0, S1, S2, S3, S3, S3, S3, S3, S3 },
+  { S0, S1, S2, S3, S4, S4, S4, S4, S4 },
+  { S0, S1, S2, S3, S4, S5, S5, S5, S5 },
+  { S0, S1, S2, S3, S4, S5, S6, S6, S6 },
+  { S0, S1, S2, S3, S4, S5, S6, S7, S7 },
+  { S0, S1, S2, S3, S4, S5, S6, S7, S8 }
+};
+
+const struct S s = { S0, S1, S2, S3, S4, S5, S6, S7, S8 };
+
+const struct S sa[9] = {
+  { S0, S0, S0, S0, S0, S0, S0, S0, S0 },
+  { S0, S1, S1, S1, S1, S1, S1, S1, S1 },
+  { S0, S1, S2, S2, S2, S2, S2, S2, S2 },
+  { S0, S1, S2, S3, S3, S3, S3, S3, S3 },
+  { S0, S1, S2, S3, S4, S4, S4, S4, S4 },
+  { S0, S1, S2, S3, S4, S5, S5, S5, S5 },
+  { S0, S1, S2, S3, S4, S5, S6, S6, S6 },
+  { S0, S1, S2, S3, S4, S5, S6, S7, S7 },
+  { S0, S1, S2, S3, S4, S5, S6, S7, S8 }
+};
+
+const struct S sa3_5_7[3][5][7] = {
+  [1][2][3].a2 = S1, [1][3][5].a3 = S2, [2][4][5].a4 = S3
+};
+
+
+void test_global_array (void)
+{
+  T (a9[0], 0);  T (a9[0] + 0, 0);  T (a9[0] + 0, 0);  T (a9[0] + 0, 0);
+  T (a9[1], 1);  T (a9[1] + 1, 0);  T (a9[1] + 1, 0);  T (a9[1] + 1, 0);
+  T (a9[2], 2);
+  T (a9[2] + 1, 1);
+  T (a9[2] + 2, 0);
+  T (a9[2] + 2, 0);
+
+  T (a9[3], 3);  T (a9[3] + 1, 2);  T (a9[3] + 2, 1);  T (a9[3] + 3, 0);
+  T (a9[4], 4);  T (a9[4] + 1, 3);  T (a9[4] + 2, 2);  T (a9[4] + 3, 1);
+  T (a9[5], 5);  T (a9[5] + 1, 4);  T (a9[5] + 2, 3);  T (a9[5] + 3, 2);
+  T (a9[6], 6);  T (a9[6] + 1, 5);  T (a9[6] + 2, 4);  T (a9[6] + 3, 3);
+  T (a9[7], 7);  T (a9[7] + 1, 6);  T (a9[7] + 2, 5);  T (a9[7] + 3, 4);
+  T (a9[8], 8);  T (a9[8] + 1, 7);  T (a9[8] + 2, 6);  T (a9[8] + 3, 5);
+
+  T (a_1_9[0], 8);
+  T (a_1_9[0] + 1, 7);
+  T (a_1_9[0] + 7, 1);
+  T (a_1_9[0] + 8, 0);
+
+  T (a_2_9[0], 8);
+  T (a_2_9[0] + 1, 7);
+  T (a_2_9[0] + 7, 1);
+  T (a_2_9[0] + 8, 0);
+
+  T (a_2_9[1], 7);
+  T (a_2_9[1] + 1, 6);
+  T (a_2_9[1] + 6, 1);
+  T (a_2_9[1] + 7, 0);
+  T (a_2_9[1] + 8, 0);
+}
+
+void test_global_array_array (void)
+{
+  T (a9_9[0][0], 0);  T (a9_9[1][0], 0);  T (a9_9[2][0], 0);
+  T (a9_9[0][1], 0);  T (a9_9[1][1], 1);  T (a9_9[2][1], 1);
+  T (a9_9[0][2], 0);  T (a9_9[1][2], 1);  T (a9_9[2][2], 2);
+  T (a9_9[0][3], 0);  T (a9_9[1][3], 1);  T (a9_9[2][3], 2);
+  T (a9_9[0][4], 0);  T (a9_9[1][4], 1);  T (a9_9[2][4], 2);
+  T (a9_9[0][5], 0);  T (a9_9[1][5], 1);  T (a9_9[2][5], 2);
+  T (a9_9[0][6], 0);  T (a9_9[1][6], 1);  T (a9_9[2][6], 2);
+  T (a9_9[0][7], 0);  T (a9_9[1][7], 1);  T (a9_9[2][7], 2);
+  T (a9_9[0][8], 0);  T (a9_9[1][8], 1);  T (a9_9[2][8], 2);
+
+  T (a9_9[3][0], 0);  T (a9_9[4][0], 0);  T (a9_9[5][0], 0);
+  T (a9_9[3][1], 1);  T (a9_9[4][1], 1);  T (a9_9[5][1], 1);
+  T (a9_9[3][2], 2);  T (a9_9[4][2], 2);  T (a9_9[5][2], 2);
+  T (a9_9[3][3], 3);  T (a9_9[4][3], 3);  T (a9_9[5][3], 3);
+  T (a9_9[3][4], 3);  T (a9_9[4][4], 4);  T (a9_9[5][4], 4);
+  T (a9_9[3][5], 3);  T (a9_9[4][5], 4);  T (a9_9[5][5], 5);
+  T (a9_9[3][6], 3);  T (a9_9[4][6], 4);  T (a9_9[5][6], 5);
+  T (a9_9[3][7], 3);  T (a9_9[4][7], 4);  T (a9_9[5][7], 5);
+  T (a9_9[3][8], 3);  T (a9_9[4][8], 4);  T (a9_9[5][8], 5);
+
+  T (a9_9[6][0], 0);  T (a9_9[7][0], 0);  T (a9_9[8][0], 0);
+  T (a9_9[6][1], 1);  T (a9_9[7][1], 1);  T (a9_9[8][1], 1);
+  T (a9_9[6][2], 2);  T (a9_9[7][2], 2);  T (a9_9[8][2], 2);
+  T (a9_9[6][3], 3);  T (a9_9[7][3], 3);  T (a9_9[8][3], 3);
+  T (a9_9[6][4], 4);  T (a9_9[7][4], 4);  T (a9_9[8][4], 4);
+  T (a9_9[6][5], 5);  T (a9_9[7][5], 5);  T (a9_9[8][5], 5);
+  T (a9_9[6][6], 6);  T (a9_9[7][6], 6);  T (a9_9[8][6], 6);
+  T (a9_9[6][7], 6);  T (a9_9[7][7], 7);  T (a9_9[8][7], 7);
+  T (a9_9[6][8], 6);  T (a9_9[7][8], 7);  T (a9_9[8][8], 8);
+
+
+  T (a9_9[0][0] + 1, 0);  T (a9_9[1][0] + 1, 0);  T (a9_9[2][0] + 2, 0);
+  T (a9_9[0][1] + 2, 0);  T (a9_9[1][1] + 1, 0);  T (a9_9[2][1] + 2, 0);
+  T (a9_9[0][2] + 3, 0);  T (a9_9[1][2] + 1, 0);  T (a9_9[2][2] + 2, 0);
+  T (a9_9[0][3] + 4, 0);  T (a9_9[1][3] + 1, 0);  T (a9_9[2][3] + 2, 0);
+  T (a9_9[0][4] + 5, 0);  T (a9_9[1][4] + 1, 0);  T (a9_9[2][4] + 2, 0);
+  T (a9_9[0][5] + 6, 0);  T (a9_9[1][5] + 1, 0);  T (a9_9[2][5] + 2, 0);
+  T (a9_9[0][6] + 7, 0);  T (a9_9[1][6] + 1, 0);  T (a9_9[2][6] + 2, 0);
+  T (a9_9[0][7] + 8, 0);  T (a9_9[1][7] + 1, 0);  T (a9_9[2][7] + 2, 0);
+}
+
+void test_global_struct (void)
+{
+  T (s.a1, 0);
+  T (s.a2, 1);
+  T (s.a3, 2);
+  T (s.a4, 3);
+  T (s.a5, 4);
+  T (s.a6, 5);
+  T (s.a7, 6);
+  T (s.a8, 7);
+  T (s.a9, 8);
+}
+
+void test_global_struct_array (void)
+{
+  T (sa[0].a1, 0);  T (sa[1].a1, 0);  T (sa[2].a1, 0);  T (sa[3].a1, 0);
+  T (sa[0].a2, 0);  T (sa[1].a2, 1);  T (sa[2].a2, 1);  T (sa[3].a2, 1);
+  T (sa[0].a3, 0);  T (sa[1].a3, 1);  T (sa[2].a3, 2);  T (sa[3].a3, 2);
+  T (sa[0].a4, 0);  T (sa[1].a4, 1);  T (sa[2].a4, 2);  T (sa[3].a4, 3);
+  T (sa[0].a5, 0);  T (sa[1].a5, 1);  T (sa[2].a5, 2);  T (sa[3].a5, 3);
+  T (sa[0].a6, 0);  T (sa[1].a6, 1);  T (sa[2].a6, 2);  T (sa[3].a6, 3);
+  T (sa[0].a7, 0);  T (sa[1].a7, 1);  T (sa[2].a7, 2);  T (sa[3].a7, 3);
+  T (sa[0].a8, 0);  T (sa[1].a8, 1);  T (sa[2].a8, 2);  T (sa[3].a8, 3);
+  T (sa[0].a9, 0);  T (sa[1].a9, 1);  T (sa[2].a9, 2);  T (sa[3].a9, 3);
+
+  T (sa[4].a1, 0);  T (sa[5].a1, 0);  T (sa[6].a1, 0);  T (sa[7].a1, 0);
+  T (sa[4].a2, 1);  T (sa[5].a2, 1);  T (sa[6].a2, 1);  T (sa[7].a2, 1);
+  T (sa[4].a3, 2);  T (sa[5].a3, 2);  T (sa[6].a3, 2);  T (sa[7].a3, 2);
+  T (sa[4].a4, 3);  T (sa[5].a4, 3);  T (sa[6].a4, 3);  T (sa[7].a4, 3);
+  T (sa[4].a5, 4);  T (sa[5].a5, 4);  T (sa[6].a5, 4);  T (sa[7].a5, 4);
+  T (sa[4].a6, 4);  T (sa[5].a6, 5);  T (sa[6].a6, 5);  T (sa[7].a6, 5);
+  T (sa[4].a7, 4);  T (sa[5].a7, 5);  T (sa[6].a7, 6);  T (sa[7].a7, 6);
+  T (sa[4].a8, 4);  T (sa[5].a8, 5);  T (sa[6].a8, 6);  T (sa[7].a8, 7);
+  T (sa[4].a9, 4);  T (sa[5].a9, 5);  T (sa[6].a9, 6);  T (sa[7].a9, 7);
+
+  T (sa[8].a1, 0);
+  T (sa[8].a2, 1);  T (sa[8].a2 + 1, 0);
+  T (sa[8].a3, 2);  T (sa[8].a3 + 1, 1);  T (sa[8].a3 + 2, 0);
+  T (sa[8].a4, 3);  T (sa[8].a4 + 1, 2);  T (sa[8].a4 + 2, 1);
+  T (sa[8].a5, 4);  T (sa[8].a5 + 1, 3);  T (sa[8].a5 + 2, 2);
+  T (sa[8].a6, 5);  T (sa[8].a6 + 1, 4);  T (sa[8].a6 + 2, 3);
+  T (sa[8].a7, 6);  T (sa[8].a7 + 1, 5);  T (sa[8].a7 + 2, 4);
+  T (sa[8].a8, 7);  T (sa[8].a8 + 1, 6);  T (sa[8].a8 + 2, 5);
+  T (sa[8].a9, 8);  T (sa[8].a9 + 1, 7);  T (sa[8].a9 + 2, 6);
+
+
+  T (sa3_5_7[1][2][3].a2, 1);
+  T (sa3_5_7[1][3][5].a3, 2);
+  T (sa3_5_7[2][4][5].a4, 3);
+
+  T (sa3_5_7[0][0][0].a1, 0);
+  T (sa3_5_7[0][0][0].a2, 0);
+  T (sa3_5_7[0][0][0].a3, 0);
+  T (sa3_5_7[0][0][0].a4, 0);
+  T (sa3_5_7[0][0][0].a5, 0);
+  T (sa3_5_7[0][0][0].a6, 0);
+  T (sa3_5_7[0][0][0].a7, 0);
+  T (sa3_5_7[0][0][0].a8, 0);
+  T (sa3_5_7[0][0][0].a9, 0);
+
+  T (sa3_5_7[0][0][1].a1, 0);
+  T (sa3_5_7[0][0][1].a2, 0);
+  T (sa3_5_7[0][0][1].a3, 0);
+  T (sa3_5_7[0][0][1].a4, 0);
+  T (sa3_5_7[0][0][1].a5, 0);
+  T (sa3_5_7[0][0][1].a6, 0);
+  T (sa3_5_7[0][0][1].a7, 0);
+  T (sa3_5_7[0][0][1].a8, 0);
+  T (sa3_5_7[0][0][1].a9, 0);
+
+  T (sa3_5_7[0][1][0].a1, 0);
+  T (sa3_5_7[0][1][0].a2, 0);
+  T (sa3_5_7[0][1][0].a3, 0);
+  T (sa3_5_7[0][1][0].a4, 0);
+  T (sa3_5_7[0][1][0].a5, 0);
+  T (sa3_5_7[0][1][0].a6, 0);
+  T (sa3_5_7[0][1][0].a7, 0);
+  T (sa3_5_7[0][1][0].a8, 0);
+  T (sa3_5_7[0][1][0].a9, 0);
+
+  T (sa3_5_7[1][0][0].a1, 0);
+  T (sa3_5_7[1][0][0].a2, 0);
+  T (sa3_5_7[1][0][0].a3, 0);
+  T (sa3_5_7[1][0][0].a4, 0);
+  T (sa3_5_7[1][0][0].a5, 0);
+  T (sa3_5_7[1][0][0].a6, 0);
+  T (sa3_5_7[1][0][0].a7, 0);
+  T (sa3_5_7[1][0][0].a8, 0);
+  T (sa3_5_7[1][0][0].a9, 0);
+}
+
+
+struct SS {
+  char a9[9][9];
+  struct S sa9[9];
+};
+
+const struct SS ssa[] = {
+  [1] = {
+    .a9 = { [3] = S3, [7] = S7 },
+    .sa9 = { [5] = { .a5 = S4, .a7 = S6 } }
+  },
+  [5] = {
+    .a9 = { [1] = S8, [5] = S4 },
+    .sa9 = { [3] = { .a3 = S2, .a6 = S3 } }
+  }
+};
+
+void test_global_struct_struct_array (void)
+{
+  T (ssa[0].a9[0], 0);
+  T (ssa[0].a9[3], 0);
+  T (ssa[0].sa9[5].a5, 0);
+  T (ssa[0].sa9[5].a7, 0);
+
+  T (ssa[1].a9[0], 0);
+
+  T (ssa[1].a9[3], 3);
+  T (ssa[1].a9[7], 7);
+  T (ssa[1].sa9[5].a5, 4);
+  T (ssa[1].sa9[5].a7, 6);
+
+  T (ssa[2].a9[3], 0);
+  T (ssa[2].a9[7], 0);
+  T (ssa[2].sa9[5].a5, 0);
+  T (ssa[2].sa9[5].a7, 0);
+
+  T (ssa[5].a9[1], 8);
+  T (ssa[5].a9[5], 4);
+  T (ssa[5].sa9[3].a3, 2);
+  T (ssa[5].sa9[3].a6, 3);
+}
+
+/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
+   { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-50.c b/gcc/testsuite/gcc.dg/strlenopt-50.c
new file mode 100644
index 0000000..1d1d368
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-50.c
@@ -0,0 +1,116 @@ 
+/* PR tree-optimization/86415 - strlen() not folded for substrings
+   within constant arrays
+   { dg-do compile }
+   { dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-ccp" } */
+
+#include "strlenopt.h"
+
+#define CONCAT(x, y) x ## y
+#define CAT(x, y) CONCAT (x, y)
+#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do {				\
+    extern void FAILNAME (name) (void);		\
+    FAILNAME (name)();				\
+  } while (0)
+
+/* Macro to emit a call to funcation named
+     call_in_true_branch_not_eliminated_on_line_NNN()
+   for each call that's expected to be eliminated.  The dg-final
+   scan-tree-dump-time directive at the bottom of the test verifies
+   that no such call appears in output.  */
+#define ELIM(expr) \
+  if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+#define T(s, n) ELIM (strlen (s) == n)
+
+/*                              11111
+	     0 1  23 4  567 8  901234  */
+#define STR "1\00012\000123\0001234\0"
+
+const char a[]   = STR;
+const char b[20] = STR;
+
+void test_literal (void)
+{
+  /* Verify that strlen() of substrings within a string literal are
+     correctly folded.  */
+  T (STR,      1);  T (STR +  1, 0);  T (STR +  2, 2);  T (STR +  3, 1);
+  T (STR +  4, 0);  T (STR +  5, 3);  T (STR +  6, 2);  T (STR +  7, 1);
+  T (STR +  8, 0);  T (STR +  9, 4);  T (STR + 10, 3);  T (STR + 11, 2);
+  T (STR + 12, 1);  T (STR + 13, 0);  T (STR + 14, 0);
+
+  T (&(STR[0]),  1);  T (&(STR[ 1]), 0);  T (&(STR[ 2]), 2);
+  T (&(STR[ 3]), 1);  T (&(STR[ 4]), 0);  T (&(STR[ 5]), 3);
+  T (&(STR[ 6]), 2);  T (&(STR[ 7]), 1);  T (&(STR[ 8]), 0);
+  T (&(STR[ 9]), 4);  T (&(STR[10]), 3);  T (&(STR[11]), 2);
+  T (&(STR[12]), 1);  T (&(STR[13]), 0);  T (&(STR[14]), 0);
+
+  T (&(STR[0])  +  1, 0);  T (&(STR[ 1]) +  1, 2);  T (&(STR[ 2]) +  1, 1);
+  T (&(STR[ 3]) +  1, 0);  T (&(STR[ 4]) +  1, 3);  T (&(STR[ 5]) +  1, 2);
+  T (&(STR[ 6]) +  1, 1);  T (&(STR[ 7]) +  1, 0);  T (&(STR[ 8]) +  1, 4);
+  T (&(STR[ 9]) +  1, 3);  T (&(STR[10]) +  1, 2);  T (&(STR[11]) +  1, 1);
+  T (&(STR[12]) +  1, 0);  T (&(STR[13]) +  1, 0);  T (&(STR[13]) - 13, 1);
+  T (&(STR[13]) - 12, 0);  T (&(STR[13]) - 11, 2);  T (&(STR[13]) - 10, 1);
+}
+
+void test_array (void)
+{
+  /* Verify that strlen() of substrings within a fully initialized
+     array are correctly folded.  */
+  T (a,      1);  T (a +  1, 0);  T (a +  2, 2);  T (a +  3, 1);
+  T (a +  4, 0);  T (a +  5, 3);  T (a +  6, 2);  T (a +  7, 1);
+  T (a +  8, 0);  T (a +  9, 4);  T (a + 10, 3);  T (a + 11, 2);
+  T (a + 12, 1);  T (a + 13, 0);  T (a + 14, 0);
+
+  /* Verify that strlen() of substrings within a partially initialized
+     array are also correctly folded, including those referring to
+     the empty substrings in the implicitly initialized elements.  */
+  T (b,      1);  T (b +  1, 0);  T (b +  2, 2);  T (b +  3, 1);
+  T (b +  4, 0);  T (b +  5, 3);  T (b +  6, 2);  T (b +  7, 1);
+  T (b +  8, 0);  T (b +  9, 4);  T (b + 10, 3);  T (b + 11, 2);
+  T (b + 12, 1);  T (b + 13, 0);  T (b + 14, 0);  T (b + 15, 0);
+  T (b + 16, 0);  T (b + 17, 0);  T (b + 18, 0);  T (b + 19, 0);
+}
+
+void test_array_ref_plus (void)
+{
+  /* Verify that strlen() of substrings within a fully initialized
+     array referred to by array indices with offsets are correctly
+     folded.  */
+  T (&a[ 0],     1);  T (&a[ 0] + 1, 0);
+  T (&a[ 1],     0);  T (&a[ 1] + 1, 2);
+  T (&a[ 2],     2);  T (&a[ 2] + 1, 1);  T (&a[ 2] + 2, 0);
+  T (&a[ 3],     1);  T (&a[ 3] + 1, 0);
+  T (&a[ 4],     0);  T (&a[ 4] + 1, 3);
+  T (&a[ 5],     3);  T (&a[ 5] + 1, 2);
+  T (&a[ 5] + 2, 1);  T (&a[ 5] + 3, 0);  T (&a[ 5] + 4, 4);
+  T (&a[ 6],     2);  T (&a[ 6] + 1, 1);  T (&a[ 6] + 2, 0);
+  T (&a[ 7],     1);  T (&a[ 7] + 1, 0);
+  T (&a[ 8],     0);  T (&a[ 8] + 1, 4);
+  T (&a[ 9],     4);  T (&a[ 9] + 1, 3);  T (&a[ 9] + 2, 2);
+  T (&a[ 9] + 3, 1);  T (&a[ 9] + 4, 0);  T (&a[ 9] + 5, 0);
+  T (&a[10],     3);  T (&a[10] + 1, 2);  T (&a[10] + 2, 1);
+  T (&a[10] + 3, 0);  T (&a[10] + 4, 0);
+  T (&a[11],     2);  T (&a[11] + 1, 1);  T (&a[11] + 2, 0);
+  T (&a[12],     1);  T (&a[12] + 1, 0);  T (&a[12] + 2, 0);
+  T (&a[13],     0);  T (&a[13] + 1, 0);
+  T (&a[14],     0);
+}
+
+void test_array_ref (void)
+{
+  T (&a[ 0], 1);  T (&a[ 1], 0);  T (&a[ 2], 2);  T (&a[ 3], 1);
+  T (&a[ 4], 0);  T (&a[ 5], 3);  T (&a[ 6], 2);  T (&a[ 7], 1);
+  T (&a[ 8], 0);  T (&a[ 9], 4);  T (&a[10], 3);  T (&a[11], 2);
+  T (&a[12], 1);  T (&a[13], 0);  T (&a[14], 0);
+
+  T (&b[ 0], 1);  T (&b[ 1], 0);  T (&b[ 2], 2);  T (&b[ 3], 1);
+  T (&b[ 4], 0);  T (&b[ 5], 3);  T (&b[ 6], 2);  T (&b[ 7], 1);
+  T (&b[ 8], 0);  T (&b[ 9], 4);  T (&b[10], 3);  T (&b[11], 2);
+  T (&b[12], 1);  T (&b[13], 0);  T (&b[14], 0);  T (&b[15], 0);
+  T (&b[16], 0);  T (&b[17], 0);  T (&b[18], 0);  T (&b[19], 0);
+}
+
+/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } }
+   { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */