gcc3.1 now sign-extends unsigned integer function arguments on alpha

John Baldwin jhb@FreeBSD.org
Thu May 16 20:07:00 GMT 2002


In the FreeBSD kernel we have the following inline Alpha assembly function:

/*
 * Atomically compare the value stored at *p with cmpval and if the
 * two values are equal, update the value of *p with newval. Returns
 * zero if the compare failed, nonzero otherwise.
 */
static __inline u_int32_t
atomic_cmpset_32(volatile u_int32_t* p, u_int32_t cmpval, u_int32_t newval)
{
        u_int32_t ret;

        __asm __volatile (
                "1:\tldl_l %0, %1\n\t"          /* load old value */
#ifdef notyet
                "zapnot  %0,0xf,%0\n\t"         /* Chop of signed bits */
#endif
                "cmpeq %0, %2, %0\n\t"          /* compare */
                "beq %0, 2f\n\t"                /* exit if not equal */
                "mov %3, %0\n\t"                /* value to store */
                "stl_c %0, %1\n\t"              /* attempt to store */
                "beq %0, 3f\n\t"                /* if it failed, spin */
                "mb\n\t"                        /* drain to memory */
                "2:\n"                          /* done */
                ".section .text3,\"ax\"\n"      /* improve branch prediction */
                "3:\tbr 1b\n"                   /* try again */
                ".previous\n"
                : "=&r" (ret), "+m" (*p)
                : "r" (cmpval), "r" (newval)
                : "memory");

        return ret;
}

Note that the cmpval and newval arguments are unsigned.  With gcc 2.95,
when a register was passed to this function, the value was zero-extended
using the zapnot instruction.  In gcc 3.1 the register is instead
sign-extended using an addl instruction.  For gcc 2.95 we added the extra
zapnot instruction after the ldl_l in the function above to zero-extend
the value loaded from *p (ldl_l sign extends the value it reads by
default).

The atomic_cmpset_32 is used in the following snippet of code:

void
_vm_object_allocate(objtype_t type, vm_size_t size, vm_object_t object)
{
        static int object_hash_rand;
        int exp, incr;

        ...
        do {
                exp = object_hash_rand;
                object->hash_rand = exp - 129;
        } while (!atomic_cmpset_int(&object_hash_rand, exp, object->hash_rand));
        ...
}


A diff of the generated code for this snippet is below.  The gcc 3.1 code is
prefixed with '-' and the 2.95 code with '+':

         :      00 00 43 a0     ldl     t1,0(t2)
-        :      7f ff 22 20     lda     t0,-129(t1)
+        :      21 35 50 40     subq    t1,0x81,t0
         :      58 00 29 b0     stl     t0,88(s0)
-        :      01 00 3f 40     addl    t0,zero,t0
+        :      22 f6 41 48     zapnot  t1,0xf,t1
+        :      21 f6 21 48     zapnot  t0,0xf,t0
         :      00 00 83 a8     ldl_l   t3,0(t2)
         :      24 f6 81 48     zapnot  t3,0xf,t3
         :      a4 05 82 40     cmpeq   t3,t1,t3
-        :      04 00 80 e4     beq     t3,16c <_vm_object_allocate+0x104>
+        :      04 00 80 e4     beq     t3,1c4 <_vm_object_allocate+0x124>
         :      04 04 e1 47     mov     t0,t3
         :      00 00 83 b8     stl_c   t3,0(t2)
-        :      00 00 80 e4     beq     t3,168 <_vm_object_allocate+0x100>
+        :      00 00 80 e4     beq     t3,1c0 <_vm_object_allocate+0x120>
         :      00 40 00 60     mb
    
   
The lda/subq difference isn't important nor are the different offests for
the branches.  However, in the second difference, gcc 3.1 uses addl to
truncate t0, whereas 2.95 uses zapnot.  addl sign extends the value it
writes to the destination, thus it is apparent that 3.1 is sign-extending
the value even though the function prototype calls for an unsigned value.
Since our code assumed a zero-extended unsigned argument and
explicitly zero-extended the value we loaded from memory to allow for that,
3.1's change broke our function.  Personally, I wouldn't mind getting rid
of that extra zapnot, but I wish to know if this change for 3.1 was
intentional and if it is correct.  My gut tells me that sign-extending
an unsigned value isn't quite right, but I can see how you can justify
the change given that other 'long' instructions such as ldl and addl do
sign extension.

-- 

John Baldwin <jhb@FreeBSD.org>  <><  http://www.FreeBSD.org/~jhb/
"Power Users Use the Power to Serve!"  -  http://www.FreeBSD.org/



More information about the Gcc-bugs mailing list