patch for combine.c for ia64 miscompilation
Jim Wilson
wilson@cygnus.com
Fri Apr 14 16:15:00 GMT 2000
I have checked in this patch to fix a bug that showed up on the ia64 port.
Given RTL
(lshiftrt:DI (reg:DI 1000) (const_int 63))
where reg 1000 has 33 sign bit copies, and we force this to SImode, combine
was converting this to
(lshiftrt:DI (reg:DI 1000) (const_int 32))
which is wrong. The before RTL has value either 0 or 1, and after RTL
has value 0 or -1.
The problem is in the code that simplies (AND (LSHIFTRT REG C1) C2) was
missing a test to make sure that the result of the shift has more bits than
are in the mask.
Since it took me a little while to understand the current tests, I added
comments to document what they do.
Fri Apr 14 16:09:02 2000 Jim Wilson <wilson@cygnus.com>
* combine.c (force_to_mode, case LSHIFTRT): Check that shift shift
plus mask size is smaller or equal to the mode size.
Index: combine.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/combine.c,v
retrieving revision 1.121
diff -p -r1.121 combine.c
*** combine.c 2000/04/05 21:14:53 1.121
--- combine.c 2000/04/14 23:09:26
*************** force_to_mode (x, mode, mask, reg, just_
*** 6935,6944 ****
--- 6935,6951 ----
if (GET_CODE (x) == LSHIFTRT
&& GET_CODE (XEXP (x, 1)) == CONST_INT
+ /* The shift puts one of the sign bit copies in the least significant
+ bit. */
&& ((INTVAL (XEXP (x, 1))
+ num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0))))
>= GET_MODE_BITSIZE (GET_MODE (x)))
&& exact_log2 (mask + 1) >= 0
+ /* Number of bits left after the shift must be more than the mask
+ needs. */
+ && ((INTVAL (XEXP (x, 1)) + exact_log2 (mask + 1))
+ <= GET_MODE_BITSIZE (GET_MODE (x)))
+ /* Must be more sign bit copies than the mask needs. */
&& ((int) num_sign_bit_copies (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
>= exact_log2 (mask + 1)))
x = gen_binary (LSHIFTRT, GET_MODE (x), XEXP (x, 0),
Original Testcase:
It prints -1 instead of 1 for the lnot (0) call.
#include <stdio.h>
int lnot (int);
int
main (int argc, char **argv)
{
printf ("lnot (0)=%d\n", lnot (0));
printf ("lnot (1)=%d\n", lnot (1));
printf ("lnot (65536)=%d\n", lnot (65536));
exit (0);
}
int
lnot (int v)
{
char *p = (char *) &v;
int len = sizeof(v);
while (--len >= 0)
{
if (*p++)
break;
}
return len < 0;
}
More information about the Gcc-patches
mailing list