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