c/3651: shifting unsigned long on 64 bit machine incorrectly does sign extension

Richard Henderson rth@redhat.com
Tue Jul 17 02:02:00 GMT 2001


On Wed, Jul 11, 2001 at 07:09:17PM -0000, mark.dewing@intel.com wrote:
> Right shifting an unsigned long should not do a sign
> extension.  This shift on an unsigned long variable works
> okay, but doing this shift on an expression that has
> been cast to unsigned long does not.
> This problem also occurs on Alpha Tru64 with gcc 2.7.2.1
> and IRIX with gcc 2.8.1 in 64 bit mode.

I'm currently testing the following patch on alphaev6-linux.
Assuming no regressions, I'll check it in tomorrow.


r~


	* c-typeck.c (build_binary_op): Do not shorten unsigned
	right shift after sign extension.

Index: c-typeck.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-typeck.c,v
retrieving revision 1.128
diff -c -p -d -r1.128 c-typeck.c
*** c-typeck.c	2001/07/07 01:07:17	1.128
--- c-typeck.c	2001/07/17 08:56:46
*************** build_binary_op (code, orig_op0, orig_op
*** 2469,2490 ****
  	      /* We can shorten only if the shift count is less than the
  		 number of bits in the smaller type size.  */
  	      && compare_tree_int (op1, TYPE_PRECISION (TREE_TYPE (arg0))) < 0
! 	      /* If arg is sign-extended and then unsigned-shifted,
! 		 we can simulate this with a signed shift in arg's type
! 		 only if the extended result is at least twice as wide
! 		 as the arg.  Otherwise, the shift could use up all the
! 		 ones made by sign-extension and bring in zeros.
! 		 We can't optimize that case at all, but in most machines
! 		 it never happens because available widths are 2**N.  */
! 	      && (!TREE_UNSIGNED (final_type)
! 		  || unsigned_arg
! 		  || (2 * TYPE_PRECISION (TREE_TYPE (arg0))
! 		      <= TYPE_PRECISION (result_type))))
  	    {
  	      /* Do an unsigned shift if the operand was zero-extended.  */
  	      result_type
! 		= signed_or_unsigned_type (unsigned_arg,
! 					   TREE_TYPE (arg0));
  	      /* Convert value-to-be-shifted to that type.  */
  	      if (TREE_TYPE (op0) != result_type)
  		op0 = convert (result_type, op0);
--- 2469,2480 ----
  	      /* We can shorten only if the shift count is less than the
  		 number of bits in the smaller type size.  */
  	      && compare_tree_int (op1, TYPE_PRECISION (TREE_TYPE (arg0))) < 0
! 	      /* We cannot drop an unsigned shift after sign-extension.  */
! 	      && (!TREE_UNSIGNED (final_type) || unsigned_arg))
  	    {
  	      /* Do an unsigned shift if the operand was zero-extended.  */
  	      result_type
! 		= signed_or_unsigned_type (unsigned_arg, TREE_TYPE (arg0));
  	      /* Convert value-to-be-shifted to that type.  */
  	      if (TREE_TYPE (op0) != result_type)
  		op0 = convert (result_type, op0);
Index: testsuite/gcc.c-torture/execute/20010717-1.c
===================================================================
RCS file: 20010717-1.c
diff -N 20010717-1.c
*** /dev/null	Tue May  5 13:32:27 1998
--- 20010717-1.c	Tue Jul 17 01:56:46 2001
***************
*** 0 ****
--- 1,28 ----
+ static void test (unsigned long, unsigned long);
+ extern void abort (void);
+ 
+ int
+ main ()
+ {
+   int i, j;
+   unsigned long u, r1, r2;
+ 
+   i = -16;
+   j = 1;
+   u = i + j;
+ 
+   /* no sign extension upon shift */
+   r1 = u >> 1;
+   /* sign extension upon shift, but there shouldn't be */
+   r2 = ((unsigned long) (i + j)) >> 1;
+   test (r1, r2);
+ 
+   return 0;
+ }
+ 
+ static void
+ test (unsigned long x, unsigned long y)
+ {
+   if (x != y)
+     abort ();
+ }



More information about the Gcc-patches mailing list