Prathamesh's fix restores the optimization for the test case
reported in the bug (thanks!) but it isn't sufficient to bring
GCC 8 completely up to par with 7.  Prior GCC versions are able
to compute the string length in the test case below but GCC 8
cannot.

  char d[8];
  const char s[] = "1234567";

  void f (void)
  {
    __builtin_strcpy (d, s);
    if (__builtin_strlen (d) != sizeof s - 1)
      __builtin_abort ();
  }

The attached patch slightly tweaks Prathamesh's patch to also
handle the case above.  There still are outstanding cases where
folding into MEM_REF defeats the strlen pass (I've raised
a couple of new bugs to track those I noticed today) but, AFAICT,
those aren't recent regressions so they can be dealt with later
and separately.

Martin

PS I've renamed the function because I had initially thought it
also needed to handle PHIs that refer to strings of the same
length (and there isn't necessarily one "right" string to return)
but then realized that the lack of that handling isn't part of
the regression so I didn't include it but kept the new return
type and name.  When the pass is enhanced to handle PHIs and
some of the other cases it doesn't handle the function won't
need to change.
PR tree-optimization/83501 - strlen(a) not folded after strcpy(a, ...)

gcc/ChangeLog:

	PR tree-optimization/83501
	* tree-ssa-strlen.c (get_string_cst): Rename...
	(get_strlen): ...to this.  Handle global constants.
	(handle_char_store): Adjust.

gcc/testsuite/ChangeLog:

	PR tree-optimization/83501
	* gcc.dg/strlenopt-39.c: New test.

Index: gcc/testsuite/gcc.dg/strlenopt-39.c
===================================================================
--- gcc/testsuite/gcc.dg/strlenopt-39.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/strlenopt-39.c	(working copy)
@@ -0,0 +1,66 @@
+/* PR tree-optimization/83444
+   { dg-do compile }
+   { dg-options "-O2 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+#define STR "1234567"
+
+const char str[] = STR;
+
+char dst[10];
+
+void copy_from_global_str (void)
+{
+  strcpy (dst, str);
+
+  if (strlen (dst) != sizeof str - 1)
+    abort ();
+}
+
+void copy_from_local_str (void)
+{
+  const char s[] = STR;
+
+  strcpy (dst, s);
+
+  if (strlen (dst) != sizeof s - 1)
+    abort ();
+}
+
+void copy_from_local_memstr (void)
+{
+  struct {
+    char s[sizeof STR];
+  } x = { STR };
+
+  strcpy (dst, x.s);
+
+  if (strlen (dst) != sizeof x.s - 1)
+    abort ();
+}
+
+void copy_to_local_str (void)
+{
+  char d[sizeof STR];
+
+  strcpy (d, str);
+
+  if (strlen (d) != sizeof str - 1)
+    abort ();
+}
+
+void copy_to_local_memstr (void)
+{
+  struct {
+    char d[sizeof STR];
+  } x;
+
+  strcpy (x.d, str);
+
+  if (strlen (x.d) != sizeof str- 1)
+    abort ();
+}
+
+/* Verify that all calls to strlen have been eliminated.
+  { dg-final { scan-tree-dump-not "(abort|strlen) \\(\\)" "optimized" } } */
Index: gcc/tree-ssa-strlen.c
===================================================================
--- gcc/tree-ssa-strlen.c	(revision 256180)
+++ gcc/tree-ssa-strlen.c	(working copy)
@@ -2772,9 +2772,11 @@ handle_pointer_plus (gimple_stmt_iterator *gsi)
     }
 }
 
-/* Check if RHS is string_cst possibly wrapped by mem_ref.  */
-static tree
-get_string_cst (tree rhs)
+/* If RHS, either directly or indirectly, refers to a string of constant
+   length, return it.  Otherwise return a negative value.  */
+
+static int
+get_strlen (tree rhs)
 {
   if (TREE_CODE (rhs) == MEM_REF
       && integer_zerop (TREE_OPERAND (rhs, 1)))
@@ -2784,7 +2786,17 @@ handle_pointer_plus (gimple_stmt_iterator *gsi)
 	rhs = TREE_OPERAND (rhs, 0);
     }
 
-  return (TREE_CODE (rhs) == STRING_CST) ? rhs : NULL_TREE;
+  if (TREE_CODE (rhs) == VAR_DECL
+      && TREE_READONLY (rhs))
+    rhs = DECL_INITIAL (rhs);
+
+  if (rhs && TREE_CODE (rhs) == STRING_CST)
+    {
+      unsigned HOST_WIDE_INT ilen = strlen (TREE_STRING_POINTER (rhs));
+      return ilen <= INT_MAX ? ilen : -1;
+    }
+
+  return -1;
 }
 
 /* Handle a single character store.  */
@@ -2799,6 +2811,9 @@ handle_char_store (gimple_stmt_iterator *gsi)
   tree rhs = gimple_assign_rhs1 (stmt);
   unsigned HOST_WIDE_INT offset = 0;
 
+  /* Set to the length of the string being assigned if known.  */
+  int rhslen;
+
   if (TREE_CODE (lhs) == MEM_REF
       && TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME)
     {
@@ -2942,19 +2957,18 @@ handle_char_store (gimple_stmt_iterator *gsi)
 	}
     }
   else if (idx == 0
-	   && (rhs = get_string_cst (gimple_assign_rhs1 (stmt)))
+	   && (rhslen = get_strlen (gimple_assign_rhs1 (stmt))) >= 0
 	   && ssaname == NULL_TREE
 	   && TREE_CODE (TREE_TYPE (lhs)) == ARRAY_TYPE)
     {
-      size_t l = strlen (TREE_STRING_POINTER (rhs));
       HOST_WIDE_INT a = int_size_in_bytes (TREE_TYPE (lhs));
-      if (a > 0 && (unsigned HOST_WIDE_INT) a > l)
+      if (a > 0 && (unsigned HOST_WIDE_INT) a > (unsigned HOST_WIDE_INT) rhslen)
 	{
 	  int idx = new_addr_stridx (lhs);
 	  if (idx != 0)
 	    {
 	      si = new_strinfo (build_fold_addr_expr (lhs), idx,
-				build_int_cst (size_type_node, l), true);
+				build_int_cst (size_type_node, rhslen), true);
 	      set_strinfo (idx, si);
 	      si->dont_invalidate = true;
 	    }

Reply via email to