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