Atomic swap
Marcel Moolenaar
marcel at xcllnt.net
Thu Aug 7 11:55:09 PDT 2003
On Thu, Aug 07, 2003 at 01:44:18PM -0400, Daniel Eischen wrote:
> How about this?
>
> static __inline void
> atomic_swap_long(volatile long *dst, long val, long *res)
> {
> u_int64_t result, temp;
>
> __asm __volatile (
> "1:\tldq %1, %3\n\t" /* load value to store */
> "ldq_l %0, %2\n\t" /* load current value, asserting lock */
> "stq_c %1, %2\n\t" /* attempt to store */
> "beq %1, 2f\n\t" /* if the store failed, spin */
> "br 3f\n" /* it worked, exit */
> "2:\tbr 1b\n" /* *dst not updated, loop */
> "3:\n" /* it worked */
> : "=&r" (result), "=&r" (temp)
> : "m" (*dst), "m" (val)
> : "memory");
>
> *res = result;
> }
The first instruction is wrong. "val" isn't memory. Also,
forget about the branch prediction optimization. It just
makes the code unreadable and we don't even know if it
makes a difference.
The following has been written down without testing (I
dropped the cosmetic \t and instead indented by hand to
make the source code readable, not what is given to the
assembler (per se):
static __inline void
atomic_swap_long(volatile long *dst, long val, long *res)
{
__asm ( "1: ldq_l t0,%0\n"
" mov %1,t1\n"
" stq_c t1,%0\n"
" beq t1,1b\n"
" stq t0,%3\n"
:: "m"(*dst), "r"(val), "m"(*res) : "memory");
}
In words:
o Read the current value at *dst in register t0 and lock the address.
o Since stq_c clobbers it's input and we may need to loop, save val
in temporary register t1.
o Store val at *dst (by means of t1) and if the lock failed, retry.
o Write the old value in register t0 to *res.
--
Marcel Moolenaar USPA: A-39004 marcel at xcllnt.net
More information about the freebsd-alpha
mailing list