Cmpxchg example for a 64-bit integer

I use cmpxchg (comparison and exchange) in the i686 architecture for 32-bit comparison and replacement as follows.

static int CAS(int *ptr, int oldVal, int newVal) { unsigned char ret; __asm__ __volatile__ ( " lock\n" " cmpxchgl %2,%1\n" " sete %0\n" : "=q" (ret), "=m" (*ptr) : "r" (newVal), "m" (*ptr), "a" (oldVal) : "memory"); //above assembly returns 0 in case of failure if (ret) return 0; } 

Which is equivalent for x86_64 architecture for 64-bit comparison and swap

 static int CAS(long *ptr, long oldVal, long newVal) { unsigned char ret; // ? if (ret) return 0; } 
+4
source share
4 answers

The x86_64 instruction set contains the cmpxchgq ( q for quadword) command for 8-byte (64-bit) compare and exchange.

There is also a cmpxchg8b command that will work with 8-byte quantities, but harder to configure, you need to use edx:eax and ecx:ebx , rather than the more natural 64-bit rax . The reason for this is almost certainly due to the fact that Intel needed 64-bit comparison and replacement operations long before x86_64 appeared. It still exists in 64-bit mode, but is no longer the only option.

But, as indicated, cmpxchgq is probably the best option for 64-bit code.


If you need a cmpxchg 16-byte object, the 64-bit version of cmpxchg8b will be cmpxchg16b . It has been missing from the earliest AMD64 processors, so compilers will not generate it for std :: atomic :: compare_exchange on 16B objects unless you enable -mcx16 (for gcc). Assemblers assemble it, however, but be careful that your binary will not work on the earliest K8 processors. (This applies only to cmpxchg16b , not to cmpxchg8b in 64-bit mode or to cmpxchgq ).

+7
source

CMPXCHG8B

 __forceinline int64_t interlockedCompareExchange(volatile int64_t & v,int64_t exValue,int64_t cmpValue) { __asm { mov esi,v mov ebx,dword ptr exValue mov ecx,dword ptr exValue + 4 mov eax,dword ptr cmpValue mov edx,dword ptr cmpValue + 4 lock cmpxchg8b qword ptr [esi] } } 
+3
source

The x64 architecture supports 64-bit communication using the old, old cmpexch . Or you can also use the slightly more complex cmpexch8b instruction (from AMD64 Architecture Programming Guide Volume 1: Application Programming "):

The CMPXCHG instruction compares the value in the AL or rAX register with the first (target) operand and sets the arithmetic flags (ZF, OF, SF, AF, CF, PF) according to the result. If the compared values ​​are equal, the source operand is loaded into the destination operand. If they are not equal, the first operand is loaded into the battery. CMPXCHG can be used to try to intercept the semaphore, i.e. check if its state is free, and if so, load the new value into the semaphore, making your state busy. verification and loading are performed atomically, so that parallel processes or threads that use a semaphore to access a shared object will not conflict.

The CMPXCHG8B command compares 64-bit values ​​in EDX: EAX registers with 64-bit memory. If the value is equal, the zero flag (ZF) is set and the ECX: EBX value is copied to the memory. Otherwise, the ZF flag is cleared, and the memory value is copied to EDX: EAX.

The CMPXCHG16B command compares a 128-bit value in RDX: RAX and RCX: RBX registers with a 128-bit memory location. If the values ​​are equal, the zero flag (ZF) and the RCX: RBX value are copied to memory. Otherwise, the ZF flag is cleared and the memory value is copied to rDX: rAX.

Different assembly syntaxes may require the length of operations specified in command mnemonics if the size of the operands cannot be output. This may be the case for the GCC inline assembler - I don't know.

+1
source

Using cmpxchg8B from the AMD64 V3 Architecture Programming Guide:

Compare EDX: Register EAX in a 64-bit memory location. If the value is equal, set the zero flag (ZF) to 1 and copy the ECX: EBX register to the memory location. Otherwise, copy the memory location to EDX: EAX and clear the zero flag.

I use cmpxchg8B to implement a simple mutex lock function on an x86-64 machine. here is the code

 .text .align 8 .global mutex_lock mutex_lock: pushq %rbp movq %rsp, %rbp jmp .L1 .L1: movl $0, %edx movl $0, %eax movl $0, %ecx movl $1, %ebx lock cmpxchg8B (%rdi) jne .L1 popq %rbp ret 
0
source

All Articles