Exchange is easy; you don't have to return any status or anything. It xchg
always swaps, so you just have to return the old value and/or a status like for cmpxchg.
static inline
int sync_xchg(int *p, int newval)
{
// xchg mem,reg has an implicit lock prefix
asm volatile("xchg %[newval], %[mem]"
: [mem] "+m" (*p), [newval] "+r" (newval) // read/write operands
:
: "memory" // compiler memory barrier, giving us seq-cst memory ordering
);
return newval;
}
It might be better to use int32_t
, but int is 32 bits in all relevant ABIs.
Also, this asm works for any size of integer. GCC will pick a 16-bit, 32-bit, or 64-bit register, and that will imply that operand-size for xchg. (You only need a suffix like addl
when there's no register operand, e.g. lock; addl $1, (%0)
)
Anyway, I tested this and it compiles correctly with gcc4.1.2 on the Godbolt compiler explorer. I don't have access to gcc4.1.1, but hopefully "+m"
and [named]
asm memory operands weren't new in that release.
+m
makes it much easier to write a bulletproof swap function. e.g. no risk of gcc choosing two different memory locations for the same variable as an input vs. as an output.
gcc -O3
output from gcc4.1.2:
# 32-bit: -m32 -fomit-frame-pointer
sync_xchg(int*, int):
movl 4(%esp), %edx
movl 8(%esp), %eax
xchg %eax, (%edx)
ret # returns in EAX
# 64-bit: just -O3
sync_xchg(int*, int):
xchg %esi, (%rdi)
movl %esi, %eax
ret # returns in EAX
I also checked that this asm actually assembles, using a newer gcc and clicking the 11010
binary button on Godbolt. Fun fact: because GAS accepts xchg mem,reg or xchg reg,mem, you can compile this function with AT&T or Intel syntax (-masm=intel
). Your other functions won't work, though.