On Thu, 27 Apr 2017, Alexander Monakov wrote:

> On Thu, 27 Apr 2017, Richard Biener wrote:
> > struct q { int n; long o[100]; };
> > struct r { int n; long o[0]; };
> > 
> > union {
> >     struct r r;
> >     struct q q;
> > } u;
> > 
> > int foo (int i, int j)
> > {
> >   long *q = u.r.o;
> >   u.r.o[i/j] = 1;
> >   return q[2];
> > }
> > 
> > but nothing convinced scheduling to move the load before the store ;)
> > The two memory references are seen as not aliasing though.  Stupid
> > scheduler.
> 
> On x86 there's an anti-dependence on %eax that prevents the second scheduler
> from performing the breaking motion; with -fschedule-insns, pre-RA scheduler
> is actually able to move the load too early, but then IRA immediately undoes
> that.  Also, -fselective-scheduling2 is able to move the load early via 
> renaming
> (and uncovers the miscompile, as nothing undoes it later).
> 
> Applying division to the result of the load, rather than the address of the
> store, also produces code demonstrating the miscompile:
> 
> struct q { int n; long o[100]; };
> struct r { int n; long o[0]; };
> 
> union {
>     struct r r;
>     struct q q;
> } u;
> 
> int foo (int i, int j)
> {
>   long *q = u.r.o;
>   u.r.o[i] = 1;
>   return q[2]/j;
> }

Thanks.  It also is still miscompiled because array_at_struct_end_p
is somewhat confused as to what its semantics are supposed to be.
Multiple callers (including the new one) assume when it returns false
they can trust the array domain while in reality when it returns false
it says we can constrain the access even if only because we know an
underlying decls size ...

It also raises the question whether for

struct X
{
  double x;
  int a[1];
} x;

x.a is an array at struct end -- there's enough padding to provide
space for x.a[1] even though its size doesn't include that (and
whether it can depends on the alignment of 'double').  This is
sort-of what happens for the union case above -- u.r.o can be
extended into the padding (which is quite large due to u.q.o).
The question is whether we allow such access to padding in general
(not only when the array is at struct end).  Likewise do we allow, in

struct Y
{
  struct X[4][4] a;
} y;

for y.a[i][j].a[k] k == 3?  that is, allow the "last" element of
an array to be flexibly extended?  Or do we instead allow
y.a[i][j] with j == 4, thus the last dimension of a multidimensional
array to be "over-allocated"?  Or do we just allow i > 3?

There's another PR where array-bound warnings are confused by
the weird answer from array_at_struct_end_p and two other users
I skimmed would generate wrong code because they trust the array
domain after it.

Looks like I have to refactor that a bit, like with the following
which makes things a bit more explicit, and _not_ allowing to
use padding appart when using flexarrays (though that can't
happen, you can't instantiate those with decls) or the zero-size
array GNU extension.

It also corrects IMHO wrong behavior with VIEW_CONVERT_EXPRs
and the overly pessimistic handling of arrays of structs with
arrays at struct end or multi-dimensional arrays when not
dealing with the outermost dimension.

So I'm testing the following.

Richard.

2017-04-28  Richard Biener  <rguent...@suse.de>

        PR middle-end/80533
        * tree.c (array_at_struct_end_p): Handle arrays at struct
        end with size zero more conservatively.  Refactor and treat
        arrays of arrays or aggregates more strict.  Fix
        VIEW_CONVERT_EXPR handling.

        * gcc.dg/torture/pr80533.c: New testcase.

Index: gcc/tree.c
===================================================================
--- gcc/tree.c  (revision 247362)
+++ gcc/tree.c  (working copy)
@@ -13222,9 +13230,19 @@ array_ref_up_bound (tree exp)
 bool
 array_at_struct_end_p (tree ref, bool allow_compref)
 {
-  if (TREE_CODE (ref) != ARRAY_REF
-      && TREE_CODE (ref) != ARRAY_RANGE_REF
-      && (!allow_compref || TREE_CODE (ref) != COMPONENT_REF))
+  tree atype;
+
+  if (TREE_CODE (ref) == ARRAY_REF
+      || TREE_CODE (ref) == ARRAY_RANGE_REF)
+    {
+      atype = TREE_TYPE (TREE_OPERAND (ref, 0));
+      ref = TREE_OPERAND (ref, 0);
+    }
+  else if (allow_compref
+          && TREE_CODE (ref) == COMPONENT_REF
+          && TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 1))) == ARRAY_TYPE)
+    atype = TREE_TYPE (TREE_OPERAND (ref, 1));
+  else
     return false;
 
   while (handled_component_p (ref))
@@ -13232,19 +13250,43 @@ array_at_struct_end_p (tree ref, bool al
       /* If the reference chain contains a component reference to a
          non-union type and there follows another field the reference
         is not at the end of a structure.  */
-      if (TREE_CODE (ref) == COMPONENT_REF
-         && TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 0))) == RECORD_TYPE)
+      if (TREE_CODE (ref) == COMPONENT_REF)
        {
-         tree nextf = DECL_CHAIN (TREE_OPERAND (ref, 1));
-         while (nextf && TREE_CODE (nextf) != FIELD_DECL)
-           nextf = DECL_CHAIN (nextf);
-         if (nextf)
-           return false;
+         if (TREE_CODE (TREE_TYPE (TREE_OPERAND (ref, 0))) == RECORD_TYPE)
+           {
+             tree nextf = DECL_CHAIN (TREE_OPERAND (ref, 1));
+             while (nextf && TREE_CODE (nextf) != FIELD_DECL)
+               nextf = DECL_CHAIN (nextf);
+             if (nextf)
+               return false;
+           }
        }
+      /* If we have a multi-dimensional array we do not consider
+         a non-innermost dimension as flex array if the whole
+        multi-dimensional array is at struct end.
+        Same for an array of aggregates with a trailing array
+        member.  */
+      else if (TREE_CODE (ref) == ARRAY_REF)
+       return false;
+      else if (TREE_CODE (ref) == ARRAY_RANGE_REF)
+       ;
+      /* If we view an underlying object as sth else then what we
+         gathered up to now is what we have to rely on.  */
+      else if (TREE_CODE (ref) == VIEW_CONVERT_EXPR)
+       break;
+      else
+       gcc_unreachable ();
 
       ref = TREE_OPERAND (ref, 0);
     }
 
+  /* The array now is at struct end.  Treat flexible arrays and
+     zero-sized arrays as always subject to extend, even into
+     just padding constrained by an underlying decl.  */
+  if (! TYPE_SIZE (atype)
+      || integer_zerop (TYPE_SIZE (atype)))
+    return true;
+
   tree size = NULL;
 
   if (TREE_CODE (ref) == MEM_REF
Index: gcc/testsuite/gcc.dg/torture/pr80533.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/pr80533.c      (nonexistent)
+++ gcc/testsuite/gcc.dg/torture/pr80533.c      (working copy)
@@ -0,0 +1,27 @@
+/* { dg-do run } */
+
+extern void abort (void);
+
+struct q { int n; long o[100]; };
+struct r { int n; long o[0]; };
+
+union {
+    struct r r;
+    struct q q;
+} u;
+
+int __attribute__((noclone,noinline))
+foo (int i, int j)
+{
+  long *q = u.r.o;
+  u.r.o[i] = 1;
+  return q[2]/j;
+}
+
+int
+main()
+{
+  if (foo (2, 1) != 1)
+    abort ();
+  return 0;
+}

Reply via email to