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;
}