diff mbox series

Fix aliasing_component_refs WRT trailing arrays of size 0

Message ID 20190616180808.trrvddiqe5jxvgu6@kam.mff.cuni.cz
State New
Headers show
Series Fix aliasing_component_refs WRT trailing arrays of size 0 | expand

Commit Message

Jan Hubicka June 16, 2019, 6:08 p.m. UTC
Hi,
while enabling bit more of alias oracle I have noticed that the size
comparsion in aliasing_component_refs_p may lead to false negatives
when struct contain trailing array of size 0. In that case the size
of a an element of that array is bigger then size of the struct.

This is fixed by the following patch: while walking the access path we
look for component refs of arrays size 0 or NULL and if they are at the
end of struct we record them.  I added check that there is at most one
in the path passing array_at_struct_end_p.

Leter the size compares is used for following decisions
 1) whether walk path1 or path2.  Here we want to take into account
    the size of path1/2 may actually increase, so in addition to
    compare type1 and type2 we want to compare with the element size
    of trailing array.
 2) when looking for type match we still want to compare type1/type2
    because there is no way to bypass the outer structure.
    However we do not want to give up too early deciding that type
    in question is too small.
 3) When deciding whether one path can continue by another, we can
    actually use the bigger size. Note that we do not need to bother
    about zero sized array reference in the path continuation since
    memory access of ref type will not write/read into the array.

I can only construct exaple when the outer types does not match (becuase then
we do conservative offset/max_size decision) and thus the extra union but I
think the testcase is still valid C.

Bootstrapped/regtested x86_64-linux, comitted.

	* gcc.dg/tree-ssa/alias-access-path-4.c: New testcase.
	* gcc.dg/tree-ssa/alias-access-path-5.c: New testcase.

	* tree-ssa-alias.c (aliasing_component_refs_p): Watch for arrays
	at the end of structures.
diff mbox series

Patch

Index: tree-ssa-alias.c
===================================================================
--- tree-ssa-alias.c	(revision 272354)
+++ tree-ssa-alias.c	(working copy)
@@ -877,22 +877,62 @@  aliasing_component_refs_p (tree ref1,
   tree *refp;
   int same_p1 = 0, same_p2 = 0;
   bool maybe_match = false;
+  tree end_struct_ref1 = NULL, end_struct_ref2 = NULL;
 
   /* Choose bases and base types to search for.  */
   base1 = ref1;
   while (handled_component_p (base1))
-    base1 = TREE_OPERAND (base1, 0);
+    {
+      /* Generally access paths are monotous in the size of object. The
+	 exception are trailing arrays of structures. I.e.
+	   struct a {int array[0];};
+	 or
+	   struct a {int array1[0]; int array[];};
+	 Such struct has size 0 but accesses to a.array may have non-zero size.
+	 In this case the size of TREE_TYPE (base1) is smaller than
+	 size of TREE_TYPE (TREE_OPERNAD (base1, 0)).
+
+	 Because we compare sizes of arrays just by sizes of their elements,
+	 we only need to care about zero sized array fields here.  */
+      if (TREE_CODE (base1) == COMPONENT_REF
+	  && TREE_CODE (TREE_TYPE (TREE_OPERAND (base1, 1))) == ARRAY_TYPE
+	  && (!TYPE_SIZE (TREE_TYPE (TREE_OPERAND (base1, 1)))
+	      || integer_zerop (TYPE_SIZE (TREE_TYPE (TREE_OPERAND (base1, 1)))))
+	  && array_at_struct_end_p (base1))
+	{
+	  gcc_checking_assert (!end_struct_ref1);
+          end_struct_ref1 = base1;
+	}
+      base1 = TREE_OPERAND (base1, 0);
+    }
   type1 = TREE_TYPE (base1);
   base2 = ref2;
   while (handled_component_p (base2))
-    base2 = TREE_OPERAND (base2, 0);
+    {
+      if (TREE_CODE (base2) == COMPONENT_REF
+	  && TREE_CODE (TREE_TYPE (TREE_OPERAND (base2, 1))) == ARRAY_TYPE
+	  && (!TYPE_SIZE (TREE_TYPE (TREE_OPERAND (base2, 1)))
+	      || integer_zerop (TYPE_SIZE (TREE_TYPE (TREE_OPERAND (base2, 1)))))
+	  && array_at_struct_end_p (base2))
+	{
+	  gcc_checking_assert (!end_struct_ref2);
+	  end_struct_ref2 = base2;
+	}
+      base2 = TREE_OPERAND (base2, 0);
+    }
   type2 = TREE_TYPE (base2);
 
   /* Now search for the type1 in the access path of ref2.  This
      would be a common base for doing offset based disambiguation on.
      This however only makes sense if type2 is big enough to hold type1.  */
   int cmp_outer = compare_type_sizes (type2, type1);
-  if (cmp_outer >= 0)
+
+  /* If type2 is big enough to contain type1 walk its access path.
+     We also need to care of arrays at the end of structs that may extend
+     beyond the end of structure.  */
+  if (cmp_outer >= 0
+      || (end_struct_ref2
+	  && compare_type_sizes (TREE_TYPE (end_struct_ref2), type1) >= 0))
     {
       refp = &ref2;
       while (true)
@@ -900,7 +940,11 @@  aliasing_component_refs_p (tree ref1,
 	  /* We walk from inner type to the outer types. If type we see is
 	     already too large to be part of type1, terminate the search.  */
 	  int cmp = compare_type_sizes (type1, TREE_TYPE (*refp));
-	  if (cmp < 0)
+
+	  if (cmp < 0
+	      && (!end_struct_ref1
+		  || compare_type_sizes (TREE_TYPE (end_struct_ref1),
+					 TREE_TYPE (*refp)) < 0))
 	    break;
 	  /* If types may be of same size, see if we can decide about their
 	     equality.  */
@@ -957,13 +1001,18 @@  aliasing_component_refs_p (tree ref1,
     }
 
   /* If we didn't find a common base, try the other way around.  */
-  if (cmp_outer <= 0)
+  if (cmp_outer <= 0 
+      || (end_struct_ref1
+	  && compare_type_sizes (TREE_TYPE (end_struct_ref1), type1) <= 0))
     {
       refp = &ref1;
       while (true)
 	{
 	  int cmp = compare_type_sizes (type2, TREE_TYPE (*refp));
-	  if (cmp < 0)
+	  if (cmp < 0
+	      && (!end_struct_ref2
+		  || compare_type_sizes (TREE_TYPE (end_struct_ref2),
+					 TREE_TYPE (*refp)) < 0))
 	    break;
 	  /* If types may be of same size, see if we can decide about their
 	     equality.  */
@@ -1022,8 +1071,14 @@  aliasing_component_refs_p (tree ref1,
      But we can still have a path that goes B1.path1...B2.path2 with
      a part that we do not see.  So we can only disambiguate now
      if there is no B2 in the tail of path1 and no B1 on the
      tail of path2.  */
   if (compare_type_sizes (TREE_TYPE (ref2), type1) >= 0
+      && (!end_struct_ref1
+	  || compare_type_sizes (TREE_TYPE (ref2),
+		 		 TREE_TYPE (end_struct_ref1)) >= 0)
       && type_has_components_p (TREE_TYPE (ref2))
       && (base1_alias_set == ref2_alias_set
           || alias_set_subset_of (base1_alias_set, ref2_alias_set)))
@@ -1034,6 +1089,9 @@  aliasing_component_refs_p (tree ref1,
   /* If this is ptr vs. decl then we know there is no ptr ... decl path.  */
   if (!ref2_is_decl
       && compare_type_sizes (TREE_TYPE (ref1), type2) >= 0
+      && (!end_struct_ref2
+	  || compare_type_sizes (TREE_TYPE (ref1),
+		 		 TREE_TYPE (end_struct_ref2)) >= 0)
       && type_has_components_p (TREE_TYPE (ref1))
       && (base2_alias_set == ref1_alias_set
 	  || alias_set_subset_of (base2_alias_set, ref1_alias_set)))
Index: testsuite/gcc.dg/tree-ssa/alias-access-path-4.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/alias-access-path-4.c	(nonexistent)
+++ testsuite/gcc.dg/tree-ssa/alias-access-path-4.c	(working copy)
@@ -0,0 +1,24 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+struct a {int v1;
+          int v2;};
+struct b {struct a a[0];};
+union c {struct b b;};
+
+int
+test (struct b *bptr1, union c *cptr, int i, int j)
+{
+  bptr1->a[i].v1=123;
+  cptr->b.a[j].v2=1;
+  return bptr1->a[i].v1;
+}
+int
+test2 (struct b *bptr1, union c *cptr, int i, int j)
+{
+  bptr1->a[i].v1=124;
+  cptr->b.a[j].v1=1;
+  return bptr1->a[i].v1;
+}
+/* { dg-final { scan-tree-dump-times "return 123" 1 "optimized"} } */
+/* { dg-final { scan-tree-dump-not "return 124" "optimized"} } */
Index: testsuite/gcc.dg/tree-ssa/alias-access-path-5.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/alias-access-path-5.c	(nonexistent)
+++ testsuite/gcc.dg/tree-ssa/alias-access-path-5.c	(working copy)
@@ -0,0 +1,25 @@ 
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+struct a {int v1;
+          int v2;};
+struct b {int array[0]; struct a a[];};
+union c {struct b b;};
+
+int
+test (struct b *bptr1, union c *cptr, int i, int j)
+{
+  bptr1->a[i].v1=123;
+  cptr->b.a[j].v2=1;
+  return bptr1->a[i].v1;
+}
+int
+test2 (struct b *bptr1, union c *cptr, int i, int j)
+{
+  bptr1->a[i].v1=124;
+  cptr->b.a[j].v1=1;
+  return bptr1->a[i].v1;
+}
+/* { dg-final { scan-tree-dump-times "return 123" 1 "optimized"} } */
+/* { dg-final { scan-tree-dump-not "return 124" "optimized"} } */