Hi, this is a regression present on mainline and 4.6 branch, apparently a fallout of the MEM_REF introduction. get_inner_reference can be called on MEM_REFs whose base has been shifted to the left
char *output = buf; output += a; output -= 1; output[0]; and, since the constant negative offset is merged into the MEM_REF, it will be returned as the BITPOS by get_inner_reference, which wreaks havoc later in the bitfield manipulation routines which expect non-negative bit positions. Clearly nothing says that the returned BITPOS should be non-negative but, on the other hand, it de facto was in the pre-MEM_REF world for valid programs (except maybe in a very specific case in Ada). The attached patch attempts to patch things up to bring us back to the previous de facto situation. Bootstrapped/regtested on x86_64-suse-linux, OK for mainline and 4.6 branch? 2012-02-07 Eric Botcazou <ebotca...@adacore.com> PR middle-end/51994 * expr.c (get_inner_reference): If there is an offset, add a negative bit position to it (if any). 2012-02-07 Eric Botcazou <ebotca...@adacore.com> * gcc.c-torture/execute/20120207-1.c: New test. -- Eric Botcazou
Index: expr.c =================================================================== --- expr.c (revision 183864) +++ expr.c (working copy) @@ -6716,6 +6716,24 @@ get_inner_reference (tree exp, HOST_WIDE /* Otherwise, split it up. */ if (offset) { + /* Avoid returning a negative bitpos as this may wreak havoc later. */ + if (double_int_negative_p (bit_offset)) + { + double_int mask + = double_int_mask (BITS_PER_UNIT == 8 + ? 3 : exact_log2 (BITS_PER_UNIT)); + double_int tem = double_int_and_not (bit_offset, mask); + /* TEM is the bitpos rounded to BITS_PER_UNIT towards -Inf. + Subtract it to BIT_OFFSET and add it (scaled) to OFFSET. */ + bit_offset = double_int_sub (bit_offset, tem); + tem = double_int_rshift (tem, + BITS_PER_UNIT == 8 + ? 3 : exact_log2 (BITS_PER_UNIT), + HOST_BITS_PER_DOUBLE_INT, true); + offset = size_binop (PLUS_EXPR, offset, + double_int_to_tree (sizetype, tem)); + } + *pbitpos = double_int_to_shwi (bit_offset); *poffset = offset; }
/* PR middle-end/51994 */ /* Testcase by Uros Bizjak <ubiz...@gmail.com> */ extern char *strcpy (char *, const char *); extern void abort (void); char __attribute__((noinline)) test (int a) { char buf[16]; char *output = buf; strcpy (&buf[0], "0123456789"); output += a; output -= 1; return output[0]; } int main () { if (test (2) != '1') abort (); return 0; }