CMPXCHG16b CMPXCHG8b XCHG
Posted: Sat Apr 05, 2025 10:07 pm
Impliments Atomic xchg functions for asm and c backends x86 x64
CMPXCHG16b CMPXCHG8b XCHG
note CMPXCHG16b and CMPXCHG8b require memory to be aligned on 16 byte or 8 byte boundaries
use the supplied wrappers to allocate and free eg *mem = AllocateAligned(size,16)
This allows you to swap two integers atomically which can help facilitate making lock free structures
use CMPXCHG2INTS which chooses either CMPXCHG16b or CMPXCHG8b
CMPXCHG16b CMPXCHG8b XCHG
note CMPXCHG16b and CMPXCHG8b require memory to be aligned on 16 byte or 8 byte boundaries
use the supplied wrappers to allocate and free eg *mem = AllocateAligned(size,16)
This allows you to swap two integers atomically which can help facilitate making lock free structures
use CMPXCHG2INTS which chooses either CMPXCHG16b or CMPXCHG8b
Code: Select all
;Atomic xchg functions asm / c backends x86 x64
;CMPXCHG16b/CMPXCHG8b
;XCHG
;AllocateAligned allocates aligned memory required for cmpxchg16b eg *mem = AllocateAligned(size,16)
;FreeAligned
;authors idle wilbert
Structure M2INTS Align #PB_Structure_AlignC
*hi
*lo
EndStructure
Procedure XCHG(*ptr.Integer,v1)
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
!__atomic_exchange_n(&p_ptr->f_i,v_v1,__ATOMIC_SEQ_CST);
CompilerElse
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
!mov ecx,[p.p_ptr]
!mov eax,[p.v_v1]
!xchg dword [ecx],eax
CompilerElse
!mov rcx, [p.p_ptr]
!mov rax, [p.v_v1 ]
!xchg qword [rcx],rax
CompilerEndIf
CompilerEndIf
EndProcedure
Procedure _CMPXCHG16B(*target.M2INTS, *old.M2INTS, *new.M2INTS)
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
Protected result.a
!struct m128 {
! unsigned long long lo;
! unsigned long long hi;
!};
! struct m128 *old = p_old;
! struct m128 *new = p_new;
! unsigned long long dummy;
! __asm__ __volatile__
!( "lock cmpxchg16b %0; setz %1"
!: "+m" ( *p_target )
!, "=a" ( v_result )
!, "=d" ( dummy )
!: "d" ( old->hi )
!, "a" ( old->lo)
!, "c" ( new->hi )
!, "b" ( new->lo )
!: "cc", "memory"
!);
ProcedureReturn result
CompilerElse
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
Protected rbx,rdi
!mov [p.v_rbx], rbx
!xor rbx,rbx
!mov [p.v_rdi], rdi
!mov rdi, [p.p_old]
!mov rax, [rdi]
!mov rdx, [rdi+8]
!mov rdi, [p.p_new]
!mov rbx, [rdi]
!mov rcx, [rdi+8]
!mov rdi, [p.p_target]
!lock cmpxchg16b [rdi]
!setz al
!test al, al
!jnz .cas_success
!mov rdi, [p.p_old]
!mov [rdi], rax
!mov [rdi+8], rdx
!.cas_success:
!movzx rax, al
!mov rdi, [p.v_rdi]
!mov rbx, [p.v_rbx]
ProcedureReturn
CompilerElse
ProcedureReturn 0
CompilerEndIf
CompilerEndIf
EndProcedure
Procedure _CMPXCHG8B(*target.M2INTS, *old.M2INTS, *new.M2INTS)
CompilerIf #PB_Compiler_Backend = #PB_Backend_C
Protected result.a
!struct m64 {
! unsigned long lo;
! unsigned long hi;
!};
! struct m64 *old = p_old;
! struct m64 *new = p_new;
! unsigned long dummy;
! __asm__ __volatile__
!( "lock cmpxchg8b %0; setz %1"
!: "+m" ( *p_target )
!, "=a" ( v_result )
!, "=d" ( dummy )
!: "d" ( old->hi )
!, "a" ( old->lo)
!, "c" ( new->hi )
!, "b" ( new->lo )
!: "cc", "memory"
!);
ProcedureReturn result
CompilerElse
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
Protected ebx,edi
!mov [p.v_ebx], ebx
!xor ebx,ebx
!mov [p.v_edi], edi
!mov edi, [p.p_old]
!mov eax, [edi]
!mov edx, [edi+4]
!mov edi, [p.p_new]
!mov ebx, [edi]
!mov ecx, [edi+4]
!mov edi, [p.p_target]
!lock cmpxchg8b [edi]
!setz al
!test al, al
!jnz .cas_success
!mov edi, [p.p_old]
!mov [edi], eax
!mov [edi+4], edx
!.cas_success:
!movzx eax, al
!mov edi, [p.v_edi]
!mov ebx, [p.v_ebx]
ProcedureReturn
CompilerElse
ProcedureReturn 0
CompilerEndIf
CompilerEndIf
EndProcedure
Prototype CMPXCHG2INTS(*target,*cmp,*new)
Global CMPXCHG2INTS.CMPXCHG2INTS
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
CMPXCHG2INTS = @_CMPXCHG16B()
CompilerElse
CMPXCHG2INTS = @_CMPXCHG8B()
CompilerEndIf
Procedure.i AllocateAligned(size.i, align.i)
Protected *mem, *aligned
*mem = AllocateMemory(size + align+SizeOf(Integer))
If *mem
*aligned = (*mem + SizeOf(integer) + align - 1) & ~(align-1)
PokeI(*aligned - SizeOf(Integer), *mem)
ProcedureReturn *aligned
EndIf
ProcedureReturn 0
EndProcedure
Procedure FreeAligned(*aligned)
If *aligned
Protected *original = PeekI(*aligned - SizeOf(Integer))
FreeMemory(*original)
EndIf
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
Global *a.M2INTS, *b.M2INTS
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
*a = AllocateAligned(16,16)
*b = AllocateAligned(16,16)
CompilerElse
*a = AllocateAligned(8,8)
*b = AllocateAligned(8,8)
CompilerEndIf
*a\Hi = $DEADBEEF
*a\lo = $FEEDF00D
*b\hi = $FEEDBEEF
*b\lo = $F00DFEED
result = CMPXCHG2INTS(*a,*a,*b)
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
Debug Hex(*a\hi)
Debug Hex(*a\lo)
CompilerElse
Debug Hex(*a\hi,#PB_Long)
Debug Hex(*a\lo,#PB_Long)
CompilerEndIf
FreeAligned(*a)
FreeAligned(*b)
CompilerEndIf