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