On Mon, 28 Oct 2013, Jeff Law wrote:

On 10/26/13 01:15, Marc Glisse wrote:
Hello,

this patch teaches gcc that free kills the memory its argument points
to. The equality test is probably too strict, I guess we can loosen it
later (unless you have suggestions?).

Note that the corresponding code for BUILT_IN_MEMCPY and others seems
suspicious to me, it looks like it is testing for equality between a
pointer and a mem_ref, which is unlikely to happen.

Bootstrap+testsuite on x86_64-unknown-linux-gnu.

2013-10-27  Marc Glisse  <marc.gli...@inria.fr>

     PR tree-optimization/19831
gcc/
     * tree-ssa-alias.c (stmt_kills_ref_p_1): Handle BUILT_IN_FREE.

gcc/testsuite/
     * gcc.dg/tree-ssa/alias-25.c: New file.
OK for the trunk.

Thanks.

I agree the MEM_REF* and VA_END cases look strange and I think they're wrong as well. Did you happen to try fixing them and running the testsuite to see if anything fails?

ISTM your testcase could be tweaked to test conclusively if they're doing the right thing -- and regardless of what's found that test can/should make its way into the testsuite.

I checked and it does the wrong thing (I don't have the testcase handy anymore, but it shouldn't be hard to recreate one), I even wrote a patch (attached) but it is related to:
http://gcc.gnu.org/ml/gcc-patches/2013-10/msg02238.html
so it can't go in. A more limited fix (still missing many cases) would be possible. I didn't test VA_END, but it doesn't look as bad (it is the SSA_NAME case that is wrong for memcpy).


While I am posting non-submitted patches, does the following vaguely make sense? I couldn't find a testcase that exercised it without some local patches, but the idea (IIRC) is that with:
struct A { struct B b; int i; }
we can easily end up with one ao_ref.base that is a MEM_REF[p] and another one a MEM_REF[(B*)q] where p and q are of type A*, and that prevents us from noticing that p->i and q->b don't alias. Maybe I should instead find a way to get MEM_REF[q] as ao_ref.base, but if q actually points to a larger structure that starts with A it is likely to fail as well...

--- gcc/tree-ssa-alias.c        (revision 204095)
+++ gcc/tree-ssa-alias.c        (working copy)
@@ -1099,22 +1099,24 @@ indirect_refs_may_alias_p (tree ref1 ATT

   /* If both references are through the same type, they do not alias
      if the accesses do not overlap.  This does extra disambiguation
      for mixed/pointer accesses but requires strict aliasing.  */
   if ((TREE_CODE (base1) != TARGET_MEM_REF
        || (!TMR_INDEX (base1) && !TMR_INDEX2 (base1)))
       && (TREE_CODE (base2) != TARGET_MEM_REF
          || (!TMR_INDEX (base2) && !TMR_INDEX2 (base2)))
       && same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (ptrtype1)) == 1
       && same_type_for_tbaa (TREE_TYPE (base2), TREE_TYPE (ptrtype2)) == 1
-      && same_type_for_tbaa (TREE_TYPE (ptrtype1),
-                            TREE_TYPE (ptrtype2)) == 1)
+      && (same_type_for_tbaa (TREE_TYPE (ptrtype1),
+                             TREE_TYPE (ptrtype2)) == 1
+         || same_type_for_tbaa (TREE_TYPE (TREE_TYPE (ptr1)),
+                                TREE_TYPE (TREE_TYPE (ptr2))) == 1))
     return ranges_overlap_p (offset1, max_size1, offset2, max_size2);

   /* Do type-based disambiguation.  */
   if (base1_alias_set != base2_alias_set
       && !alias_sets_conflict_p (base1_alias_set, base2_alias_set))
     return false;

   /* Do access-path based disambiguation.  */
   if (ref1 && ref2
       && (handled_component_p (ref1) || handled_component_p (ref2))



In the category "ugly hack that I won't submit", let me also attach this patch that sometimes helps notice that malloc doesn't alias other things (I don't know if the first hunk ever fires).

--
Marc Glisse
Index: gcc/tree-ssa-alias.c
===================================================================
--- gcc/tree-ssa-alias.c        (revision 204089)
+++ gcc/tree-ssa-alias.c        (working copy)
@@ -1985,23 +1985,24 @@ stmt_may_clobber_ref_p (gimple stmt, tre
   return stmt_may_clobber_ref_p_1 (stmt, &r);
 }
 
 /* If STMT kills the memory reference REF return true, otherwise
    return false.  */
 
 static bool
 stmt_kills_ref_p_1 (gimple stmt, ao_ref *ref)
 {
   /* For a must-alias check we need to be able to constrain
-     the access properly.  */
-  ao_ref_base (ref);
-  if (ref->max_size == -1)
+     the access properly.
+     FIXME: except for BUILTIN_FREE.  */
+  if (!ao_ref_base (ref)
+      || ref->max_size == -1)
     return false;
 
   if (gimple_has_lhs (stmt)
       && TREE_CODE (gimple_get_lhs (stmt)) != SSA_NAME
       /* The assignment is not necessarily carried out if it can throw
         and we can catch it in the current function where we could inspect
         the previous value.
         ???  We only need to care about the RHS throwing.  For aggregate
         assignments or similar calls and non-call exceptions the LHS
         might throw as well.  */
@@ -2064,37 +2065,45 @@ stmt_kills_ref_p_1 (gimple stmt, ao_ref
          case BUILT_IN_MEMPCPY:
          case BUILT_IN_MEMMOVE:
          case BUILT_IN_MEMSET:
          case BUILT_IN_MEMCPY_CHK:
          case BUILT_IN_MEMPCPY_CHK:
          case BUILT_IN_MEMMOVE_CHK:
          case BUILT_IN_MEMSET_CHK:
            {
              tree dest = gimple_call_arg (stmt, 0);
              tree len = gimple_call_arg (stmt, 2);
-             tree base = NULL_TREE;
-             HOST_WIDE_INT offset = 0;
+             tree rbase = ref->base;
+             HOST_WIDE_INT roffset = ref->offset;
              if (!host_integerp (len, 0))
                return false;
-             if (TREE_CODE (dest) == ADDR_EXPR)
-               base = get_addr_base_and_unit_offset (TREE_OPERAND (dest, 0),
-                                                     &offset);
-             else if (TREE_CODE (dest) == SSA_NAME)
-               base = dest;
-             if (base
-                 && base == ao_ref_base (ref))
+             ao_ref dref;
+             ao_ref_init_from_ptr_and_size (&dref, dest, len);
+             tree base = ao_ref_base (&dref);
+             HOST_WIDE_INT offset = dref.offset;
+             if (!base)
+               return false;
+             if (TREE_CODE (base) == MEM_REF)
+               {
+                 if (TREE_CODE (rbase) != MEM_REF)
+                   return false;
+                 // Compare pointers.
+                 offset += 8 * TREE_INT_CST_LOW (TREE_OPERAND (base, 1));
+                 roffset += 8 * TREE_INT_CST_LOW (TREE_OPERAND (rbase, 1));
+                 base = TREE_OPERAND (base, 0);
+                 rbase = TREE_OPERAND (rbase, 0);
+               }
+             if (base == rbase)
                {
-                 HOST_WIDE_INT size = TREE_INT_CST_LOW (len);
-                 if (offset <= ref->offset / BITS_PER_UNIT
-                     && (offset + size
-                         >= ((ref->offset + ref->max_size + BITS_PER_UNIT - 1)
-                             / BITS_PER_UNIT)))
+                 HOST_WIDE_INT size = 8 * TREE_INT_CST_LOW (len);
+                 if (offset <= roffset
+                     && offset + size >= (roffset + ref->max_size))
                    return true;
                }
              break;
            }
 
          case BUILT_IN_VA_END:
            {
              tree ptr = gimple_call_arg (stmt, 0);
              if (TREE_CODE (ptr) == ADDR_EXPR)
                {
Index: gcc/tree-ssa-alias.c
===================================================================
--- gcc/tree-ssa-alias.c        (revision 204095)
+++ gcc/tree-ssa-alias.c        (working copy)
@@ -207,24 +207,33 @@ ptr_deref_may_alias_decl_p (tree ptr, tr
        return true;
     }
 
   /* Non-aliased variables can not be pointed to.  */
   if (!may_be_aliased (decl))
     return false;
 
   /* If we do not have useful points-to information for this pointer
      we cannot disambiguate anything else.  */
   pi = SSA_NAME_PTR_INFO (ptr);
-  if (!pi)
-    return true;
+  if (pi && !pt_solution_includes (&pi->pt, decl))
+    return false;
+
+  /* malloc does not alias declarations.  */
+  if (TREE_CODE (ptr) == SSA_NAME)
+    {
+      gimple stmt = SSA_NAME_DEF_STMT (ptr);
+      if (is_gimple_call (stmt)
+         && (gimple_call_return_flags (stmt) & ERF_NOALIAS))
+       return false;
+    }
 
-  return pt_solution_includes (&pi->pt, decl);
+  return true;
 }
 
 /* Return true if dereferenced PTR1 and PTR2 may alias.
    The caller is responsible for applying TBAA to see if accesses
    through PTR1 and PTR2 may conflict at all.  */
 
 bool
 ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
 {
   struct ptr_info_def *pi1, *pi2;
@@ -293,26 +302,58 @@ ptr_derefs_may_alias_p (tree ptr1, tree
   /* We may end up with two empty points-to solutions for two same pointers.
      In this case we still want to say both pointers alias, so shortcut
      that here.  */
   if (ptr1 == ptr2)
     return true;
 
   /* If we do not have useful points-to information for either pointer
      we cannot disambiguate anything else.  */
   pi1 = SSA_NAME_PTR_INFO (ptr1);
   pi2 = SSA_NAME_PTR_INFO (ptr2);
-  if (!pi1 || !pi2)
-    return true;
-
   /* ???  This does not use TBAA to prune decls from the intersection
      that not both pointers may access.  */
-  return pt_solutions_intersect (&pi1->pt, &pi2->pt);
+  if (pi1 && pi2 && !pt_solutions_intersect (&pi1->pt, &pi2->pt))
+    return false;
+
+  /* malloc cannot alias a parameter, a preexisting variable or
+     another malloc. This is a hack, we don't follow the def-use chain,
+     and this should all be part of the points-to analysis anyway.  */
+  if (in_gimple_form
+      && TREE_CODE (ptr1) == SSA_NAME && TREE_CODE (ptr2) == SSA_NAME)
+    {
+      gimple stmt1 = SSA_NAME_DEF_STMT (ptr1);
+      gimple stmt2 = SSA_NAME_DEF_STMT (ptr2);
+      int w1;
+      int w2;
+      if (is_gimple_call (stmt1)
+         && (gimple_call_return_flags (stmt1) & ERF_NOALIAS))
+       w1 = 2;
+      else
+#if 1
+       w1 = stmt_dominates_stmt_p (stmt1, stmt2);
+#else
+       w1 = gimple_nop_p (stmt1);
+#endif
+      if (is_gimple_call (stmt2)
+         && (gimple_call_return_flags (stmt2) & ERF_NOALIAS))
+       w2 = 2;
+      else
+#if 1
+       w2 = stmt_dominates_stmt_p (stmt2, stmt1);
+#else
+       w2 = gimple_nop_p (stmt2);
+#endif
+      if (w1 + w2 > 2)
+       return false;
+    }
+
+    return true;
 }
 
 /* Return true if dereferencing PTR may alias *REF.
    The caller is responsible for applying TBAA to see if PTR
    may access *REF at all.  */
 
 static bool
 ptr_deref_may_alias_ref_p_1 (tree ptr, ao_ref *ref)
 {
   tree base = ao_ref_base (ref);

Reply via email to