On Fri, Sep 30, 2011 at 12:35:49PM +0200, Richard Guenther wrote:
> On Fri, Sep 30, 2011 at 12:23 PM, Jakub Jelinek <ja...@redhat.com> wrote:
> > On Fri, Sep 30, 2011 at 10:25:35AM +0200, Richard Guenther wrote:
> >> > This patch teaches PTA/aliasing about strdup/strndup (that the passed in
> >> > string is just read and doesn't escape in any way, and that otherwise it
> >> > acts as malloc or other allocation calls.
> >> > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> >
> > Ok, here is the promissed updated patch.
> 
> Ok if it passes bootstrap & test.  The str[n]cat changes are probably
> worth backporting.

Here is the updated patch, bootstrapped/regtested on x86_64-linux and
i686-linux, committed to trunk.  Will work on 4.6 backport of the str{,n}cat
part.

2011-09-30  Jakub Jelinek  <ja...@redhat.com>
            Richard Guenther  <rguent...@suse.de>

        * tree-ssa-structalias.c (find_func_aliases_for_builtin_call): Handle
        BUILT_IN_STRDUP and BUILT_IN_STRNDUP.
        * tree-ssa-alias.c (call_may_clobber_ref_p_1): Likewise.  Fix
        handling of BUILT_IN_STRNCAT and BUILT_IN_STRNCAT_CHK.
        (ref_maybe_used_by_call_p_1): Fix handling of BUILT_IN_STRCAT,
        BUILT_IN_STRNCAT, BUILT_IN_STRCAT_CHK and BUILT_IN_STRNCAT_CHK.

        * gcc.dg/strlenopt-21.c: New test.

--- gcc/tree-ssa-structalias.c.jj       2011-09-29 15:27:17.000000000 +0200
+++ gcc/tree-ssa-structalias.c  2011-09-30 11:27:06.000000000 +0200
@@ -4130,6 +4130,24 @@ find_func_aliases_for_builtin_call (gimp
       case BUILT_IN_REMQUOL:
       case BUILT_IN_FREE:
        return true;
+      case BUILT_IN_STRDUP:
+      case BUILT_IN_STRNDUP:
+       if (gimple_call_lhs (t))
+         {
+           handle_lhs_call (t, gimple_call_lhs (t), gimple_call_flags (t),
+                            NULL, fndecl);
+           get_constraint_for_ptr_offset (gimple_call_lhs (t),
+                                          NULL_TREE, &lhsc);
+           get_constraint_for_ptr_offset (gimple_call_arg (t, 0),
+                                          NULL_TREE, &rhsc);
+           do_deref (&lhsc);
+           do_deref (&rhsc);
+           process_all_all_constraints (lhsc, rhsc);
+           VEC_free (ce_s, heap, lhsc);
+           VEC_free (ce_s, heap, rhsc);
+           return true;
+         }
+       break;
       /* Trampolines are special - they set up passing the static
         frame.  */
       case BUILT_IN_INIT_TRAMPOLINE:
--- gcc/tree-ssa-alias.c.jj     2011-09-29 15:27:17.000000000 +0200
+++ gcc/tree-ssa-alias.c        2011-09-30 12:16:20.000000000 +0200
@@ -1178,8 +1177,20 @@ ref_maybe_used_by_call_p_1 (gimple call,
       && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
     switch (DECL_FUNCTION_CODE (callee))
       {
-       /* All the following functions clobber memory pointed to by
-          their first argument.  */
+       /* All the following functions read memory pointed to by
+          their second argument.  strcat/strncat additionally
+          reads memory pointed to by the first argument.  */
+       case BUILT_IN_STRCAT:
+       case BUILT_IN_STRNCAT:
+         {
+           ao_ref dref;
+           ao_ref_init_from_ptr_and_size (&dref,
+                                          gimple_call_arg (call, 0),
+                                          NULL_TREE);
+           if (refs_may_alias_p_1 (&dref, ref, false))
+             return true;
+         }
+         /* FALLTHRU */
        case BUILT_IN_STRCPY:
        case BUILT_IN_STRNCPY:
        case BUILT_IN_MEMCPY:
@@ -1187,8 +1198,6 @@ ref_maybe_used_by_call_p_1 (gimple call,
        case BUILT_IN_MEMPCPY:
        case BUILT_IN_STPCPY:
        case BUILT_IN_STPNCPY:
-       case BUILT_IN_STRCAT:
-       case BUILT_IN_STRNCAT:
          {
            ao_ref dref;
            tree size = NULL_TREE;
@@ -1199,14 +1208,23 @@ ref_maybe_used_by_call_p_1 (gimple call,
                                           size);
            return refs_may_alias_p_1 (&dref, ref, false);
          }
+       case BUILT_IN_STRCAT_CHK:
+       case BUILT_IN_STRNCAT_CHK:
+         {
+           ao_ref dref;
+           ao_ref_init_from_ptr_and_size (&dref,
+                                          gimple_call_arg (call, 0),
+                                          NULL_TREE);
+           if (refs_may_alias_p_1 (&dref, ref, false))
+             return true;
+         }
+         /* FALLTHRU */
        case BUILT_IN_STRCPY_CHK:
        case BUILT_IN_STRNCPY_CHK:
        case BUILT_IN_MEMCPY_CHK:
        case BUILT_IN_MEMMOVE_CHK:
        case BUILT_IN_MEMPCPY_CHK:
        case BUILT_IN_STPCPY_CHK:
-       case BUILT_IN_STRCAT_CHK:
-       case BUILT_IN_STRNCAT_CHK:
          {
            ao_ref dref;
            tree size = NULL_TREE;
@@ -1226,6 +1244,19 @@ ref_maybe_used_by_call_p_1 (gimple call,
                                           size);
            return refs_may_alias_p_1 (&dref, ref, false);
          }
+       /* These read memory pointed to by the first argument.  */
+       case BUILT_IN_STRDUP:
+       case BUILT_IN_STRNDUP:
+         {
+           ao_ref dref;
+           tree size = NULL_TREE;
+           if (gimple_call_num_args (call) == 2)
+             size = gimple_call_arg (call, 1);
+           ao_ref_init_from_ptr_and_size (&dref,
+                                          gimple_call_arg (call, 0),
+                                          size);
+           return refs_may_alias_p_1 (&dref, ref, false);
+         }
        /* The following builtins do not read from memory.  */
        case BUILT_IN_FREE:
        case BUILT_IN_MALLOC:
@@ -1467,7 +1498,12 @@ call_may_clobber_ref_p_1 (gimple call, a
          {
            ao_ref dref;
            tree size = NULL_TREE;
-           if (gimple_call_num_args (call) == 3)
+           /* Don't pass in size for strncat, as the maximum size
+              is strlen (dest) + n + 1 instead of n, resp.
+              n + 1 at dest + strlen (dest), but strlen (dest) isn't
+              known.  */
+           if (gimple_call_num_args (call) == 3
+               && DECL_FUNCTION_CODE (callee) != BUILT_IN_STRNCAT)
              size = gimple_call_arg (call, 2);
            ao_ref_init_from_ptr_and_size (&dref,
                                           gimple_call_arg (call, 0),
@@ -1486,7 +1522,12 @@ call_may_clobber_ref_p_1 (gimple call, a
          {
            ao_ref dref;
            tree size = NULL_TREE;
-           if (gimple_call_num_args (call) == 4)
+           /* Don't pass in size for __strncat_chk, as the maximum size
+              is strlen (dest) + n + 1 instead of n, resp.
+              n + 1 at dest + strlen (dest), but strlen (dest) isn't
+              known.  */
+           if (gimple_call_num_args (call) == 4
+               && DECL_FUNCTION_CODE (callee) != BUILT_IN_STRNCAT_CHK)
              size = gimple_call_arg (call, 2);
            ao_ref_init_from_ptr_and_size (&dref,
                                           gimple_call_arg (call, 0),
@@ -1506,6 +1547,8 @@ call_may_clobber_ref_p_1 (gimple call, a
           being the definition point for the pointer.  */
        case BUILT_IN_MALLOC:
        case BUILT_IN_CALLOC:
+       case BUILT_IN_STRDUP:
+       case BUILT_IN_STRNDUP:
          /* Unix98 specifies that errno is set on allocation failure.  */
          if (flag_errno_math
              && targetm.ref_may_alias_errno (ref))
--- gcc/testsuite/gcc.dg/strlenopt-21.c.jj      2011-09-29 15:42:19.000000000 
+0200
+++ gcc/testsuite/gcc.dg/strlenopt-21.c 2011-09-29 15:42:00.000000000 +0200
@@ -0,0 +1,66 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-strlen -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+struct S { char *p; size_t l; };
+
+__attribute__((noinline, noclone)) struct S
+foo (char *x, int n)
+{
+  int i;
+  char a[64];
+  char *p = strchr (x, '\0');
+  struct S s;
+  /* strcpy here is optimized into memcpy, length computed as p - x + 1.  */
+  strcpy (a, x);
+  /* strcat here is optimized into memcpy.  */
+  strcat (p, "abcd");
+  for (i = 0; i < n; i++)
+    if ((i % 123) == 53)
+      /* strcat here is optimized into strlen and memcpy.  */
+      strcat (a, "efg");
+  s.p = strdup (a);
+  /* The strlen should be optimized here into 4.  */
+  s.l = strlen (p);
+  return s;
+}
+
+int
+main ()
+{
+  char buf[32];
+  struct S s;
+  buf[0] = 'z';
+  buf[1] = '\0';
+  s = foo (buf, 0);
+  if (s.l != 4 || memcmp (buf, "zabcd", 6) != 0)
+    abort ();
+  if (s.p == NULL)
+    return 0;
+  if (memcmp (s.p, "z", 2) != 0)
+    abort ();
+  s = foo (buf, 60);
+  if (s.l != 4 || memcmp (buf, "zabcdabcd", 10) != 0)
+    abort ();
+  if (s.p == NULL)
+    return 0;
+  if (memcmp (s.p, "zabcdefg", 9) != 0)
+    abort ();
+  s = foo (buf, 240);
+  if (s.l != 4 || memcmp (buf, "zabcdabcdabcd", 14) != 0)
+    abort ();
+  if (s.p == NULL)
+    return 0;
+  if (memcmp (s.p, "zabcdabcdefgefg", 16) != 0)
+    abort ();
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "memcpy \\(" 3 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "strchr \\(" 1 "strlen" } } */
+/* { dg-final { scan-tree-dump-times "stpcpy \\(" 0 "strlen" } } */
+/* { dg-final { cleanup-tree-dump "strlen" } } */


        Jakub

Reply via email to