The following demonstrates a bug in combine
(x86 -mtune=pentiumpro -O2):
struct Flags {
int filler[18];
unsigned int a:14;
unsigned int b:14;
unsigned int c:1;
unsigned int d:1;
unsigned int e:1;
unsigned int f:1;
};
extern int bar(int), baz();
int foo (struct Flags *f) {
if (f->b > 0)
return bar(f->d);
return baz();
}
The test of f->b comes out as
testl $1048512, 73(%eax)
This is wrong, because 4 bytes starting at 73 goes outside the original
object and can
cause a page fault. The change from referencing a word at offset 72 to
offset 73
happens in make_extraction in combine, and I propose to fix it thus:
Index: combine.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/combine.c,v
retrieving revision 1.502
diff -u -b -c -3 -p -r1.502 combine.c
cvs diff: conflicting specifications of output style
*** combine.c 8 Aug 2005 18:30:09 -0000 1.502
--- combine.c 25 Aug 2005 17:57:21 -0000
*************** make_extraction (enum machine_mode mode,
*** 6484,6491 ****
&& GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (is_mode))
offset -= GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (inner_mode);
! /* If this is a constant position, we can move to the desired byte. */
! if (pos_rtx == 0)
{
offset += pos / BITS_PER_UNIT;
pos %= GET_MODE_BITSIZE (wanted_inner_mode);
--- 6484,6493 ----
&& GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (is_mode))
offset -= GET_MODE_SIZE (is_mode) - GET_MODE_SIZE (inner_mode);
! /* If this is a constant position, we can move to the desired byte.
! This is unsafe for memory objects; it might result in accesses
! outside the original object. */
! if (pos_rtx == 0 && !MEM_P (inner))
{
offset += pos / BITS_PER_UNIT;
pos %= GET_MODE_BITSIZE (wanted_inner_mode);
Still testing, but I'm a bit concerned this is overkill. Are there
targets/situations where
this transformation is useful or even necessary? Comments?