Hi!

This is an updated version of the get_bit_range fix, this time it
ensures that bitpos is not negative already right after the
get_inner_reference call, because various parts of the expansion might be
confused by the negative values.  As the second testcase shows, even with
non-negative bitpos we can still end up with bitoffset > *bitpos when *offset
is NULL, so get_bit_range handles that too.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk (and
after a while 4.8)?

2013-11-05  Jakub Jelinek  <ja...@redhat.com>

        PR middle-end/58970
        * expr.c (get_bit_range): Handle *offset == NULL_TREE.
        (expand_assignment): If *bitpos is negative, set *offset
        and adjust *bitpos, so that it is not negative.

        * gcc.c-torture/compile/pr58970-1.c: New test.
        * gcc.c-torture/compile/pr58970-2.c: New test.

--- gcc/expr.c.jj       2013-11-04 11:27:35.769278058 +0100
+++ gcc/expr.c  2013-11-04 21:40:54.283437813 +0100
@@ -4574,19 +4574,19 @@ get_bit_range (unsigned HOST_WIDE_INT *b
                - tree_low_cst (DECL_FIELD_BIT_OFFSET (repr), 1));
 
   /* If the adjustment is larger than bitpos, we would have a negative bit
-     position for the lower bound and this may wreak havoc later.  This can
-     occur only if we have a non-null offset, so adjust offset and bitpos
-     to make the lower bound non-negative.  */
+     position for the lower bound and this may wreak havoc later.  Adjust
+     offset and bitpos to make the lower bound non-negative in that case.  */
   if (bitoffset > *bitpos)
     {
       HOST_WIDE_INT adjust = bitoffset - *bitpos;
-
       gcc_assert ((adjust % BITS_PER_UNIT) == 0);
-      gcc_assert (*offset != NULL_TREE);
 
       *bitpos += adjust;
-      *offset
-       = size_binop (MINUS_EXPR, *offset, size_int (adjust / BITS_PER_UNIT));
+      if (*offset == NULL_TREE)
+       *offset = size_int (-adjust / BITS_PER_UNIT);
+      else
+       *offset
+         = size_binop (MINUS_EXPR, *offset, size_int (adjust / BITS_PER_UNIT));
       *bitstart = 0;
     }
   else
@@ -4719,6 +4719,15 @@ expand_assignment (tree to, tree from, b
       tem = get_inner_reference (to, &bitsize, &bitpos, &offset, &mode1,
                                 &unsignedp, &volatilep, true);
 
+      /* Make sure bitpos is not negative, it can wreak havoc later.  */
+      if (bitpos < 0)
+       {
+         gcc_assert (offset == NULL_TREE);
+         offset = size_int (bitpos >> (BITS_PER_UNIT == 8
+                                       ? 3 : exact_log2 (BITS_PER_UNIT)));
+         bitpos &= BITS_PER_UNIT - 1;
+       }
+
       if (TREE_CODE (to) == COMPONENT_REF
          && DECL_BIT_FIELD_TYPE (TREE_OPERAND (to, 1)))
        get_bit_range (&bitregion_start, &bitregion_end, to, &bitpos, &offset);
--- gcc/testsuite/gcc.c-torture/compile/pr58970-1.c.jj  2013-11-04 
21:27:47.531590559 +0100
+++ gcc/testsuite/gcc.c-torture/compile/pr58970-1.c     2013-11-04 
21:27:47.531590559 +0100
@@ -0,0 +1,11 @@
+/* PR middle-end/58970 */
+
+struct T { int b : 1; };
+struct S { struct T t[1]; };
+
+void
+foo (int x, struct S *s)
+{
+  if (x == -1)
+    s->t[x].b = 0;
+}
--- gcc/testsuite/gcc.c-torture/compile/pr58970-2.c.jj  2013-08-25 
18:20:55.717911035 +0200
+++ gcc/testsuite/gcc.c-torture/compile/pr58970-2.c     2013-11-04 
21:45:41.997935365 +0100
@@ -0,0 +1,11 @@
+/* PR middle-end/58970 */
+
+struct T { char a : 8; char b : 1; };
+struct S { char x; struct T t[1]; };
+
+void
+foo (int x, struct S *s)
+{
+  if (x == -1)
+    s->t[x].b = 0;
+}

        Jakub

Reply via email to