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