CMPXCHG16b CMPXCHG8b XCHG

Share your advanced PureBasic knowledge/code with the community.
User avatar
idle
Always Here
Always Here
Posts: 5888
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

CMPXCHG16b CMPXCHG8b XCHG

Post by idle »

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

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 


Quin
Addict
Addict
Posts: 1132
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: CMPXCHG16b CMPXCHG8b XCHG

Post by Quin »

Idle, you never fail to absolutely blow my mind.
Where did you gain such a deep knowledge of assembly? I am in awe. :D
I can read assembly but I could not even come close to being able to write something like this. Great job as always and thanks for sharing 8)
User avatar
idle
Always Here
Always Here
Posts: 5888
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: CMPXCHG16b CMPXCHG8b XCHG

Post by idle »

Quin wrote: Sun Apr 06, 2025 2:13 am Idle, you never fail to absolutely blow my mind.
Where did you gain such a deep knowledge of assembly? I am in awe. :D
I can read assembly but I could not even come close to being able to write something like this. Great job as always and thanks for sharing 8)
I mostly learnt by trying and having people around like wilbert who's kind to help me but I'm pretty much a noob at asm
Post Reply