]> git.saurik.com Git - apple/libc.git/blobdiff - x86_64/sys/OSAtomic.s
Libc-763.11.tar.gz
[apple/libc.git] / x86_64 / sys / OSAtomic.s
index b4c5e9c44606ea49264f60f2d0d587f985def8d8..2f18fb96a621d634dbbb4cfe08fd8f972a8fad6e 100644 (file)
  */
 
 #include <machine/cpu_capabilities.h>
+#include <platfunc.h>
 
 #define DECLARE(x)   \
-.align 2, 0x90      ; \
-.globl x            ; \
-.globl x ## Barrier ; \
+       .align 2, 0x90      ; \
+       .globl x            ; \
+       .globl x ## Barrier ; \
 x:                  ; \
 x ## Barrier:
 
 .text
 
+#define ATOMIC_UP      0
+#define ATOMIC_MP      1
+#define ATOMIC_RET_ORIG        0
+#define ATOMIC_RET_NEW 1
+
+// compare and exchange 32-bit
+// xchg32 <new> <dst> <mp>
+.macro xchg32
+       .if $2 == ATOMIC_MP
+       lock
+       .endif
+       cmpxchgl        $0, ($1)
+.endm
+
+// xchg64 <new> <dst> <mp>
+.macro xchg64
+       .if $2 == ATOMIC_MP
+       lock
+       .endif
+       cmpxchg         $0, ($1)
+.endm
+
+#define        ATOMIC_ARITHMETIC(instr, orig, mp)      \
+       movl    (%rsi), %eax    /* get 2nd arg -> eax */ ;\
+1:     movl    %eax, %edx      /* copy value to new reg */ ;\
+       instr   %edi, %edx      /* apply instr to %edx with arg2 */ ;\
+       xchg32  %edx, %rsi, mp  /* do the compare swap (see macro above) */ ;\
+       jnz     1b              /* jump if failed */ ;\
+       .if orig == 1           /* to return the new value, overwrite eax */ ;\
+       movl    %edx, %eax      /* return the new value */ ;\
+       .endif
+
+// Used in OSAtomicTestAndSet( uint32_t n, void *value ), assumes ABI parameter loctions
+// Manpage says bit to test/set is (0x80 >> (n & 7)) of byte (addr + (n >> 3))
+#define        ATOMIC_BIT_OP(instr, mp)        \
+       xorl    $7, %edi        /* bit position is numbered big endian so convert to little endian */ ;\
+       shlq    $3, %rsi        ;\
+       addq    %rdi, %rsi      /* generate bit address */ ;\
+       movq    %rsi, %rdi      ;\
+       andq    $31, %rdi       /* keep bit offset in range 0..31 */ ;\
+       xorq    %rdi, %rsi      /* 4-byte align address */ ;\
+       shrq    $3, %rsi        /* get 4-byte aligned address */ ;\
+       .if mp == ATOMIC_MP     /* don't plant the lock in UP code */ ;\
+       lock                    /* lock the bit test */ ;\
+       .endif                  ;\
+       instr   %edi, (%rsi)    /* do the bit test, supplied into the macro */ ;\
+       setc    %al             ;\
+       movzbl  %al,%eax        /* widen in case caller assumes we return an int */
 
 // uint32_t OSAtomicAnd32( uint32_t mask, uint32_t *value);
-DECLARE(_OSAtomicAnd32)
-       movq    $(_COMM_PAGE_COMPARE_AND_SWAP32), %rcx
-       movl    %edi, %r11d     // save mask
-       movl    (%rsi), %eax    // get value
-       movq    %rsi, %rdx      // put ptr where compare-and-swap expects it
-1:
-       movl    %r11d, %esi     // original mask
-       movl    %eax, %edi      // old value
-       andl    %eax, %esi      // new value
-       call    *%rcx           // %edi=old value,  %esi=new value. %rdx=ptr
-       jnz     1b
-       movl    %esi, %eax
+PLATFUNC_FUNCTION_START(OSAtomicAnd32, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicAnd32Barrier, up, 64, 2)
+       ATOMIC_ARITHMETIC(andl, ATOMIC_RET_NEW, ATOMIC_UP)
+       ret
+       
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32Barrier, mp, 64, 2)
+       ATOMIC_ARITHMETIC(andl, ATOMIC_RET_NEW, ATOMIC_MP)
        ret
-
 
 // uint32_t OSAtomicOr32( uint32_t mask, uint32_t *value);
-DECLARE(_OSAtomicOr32)
-       movq    $(_COMM_PAGE_COMPARE_AND_SWAP32), %rcx
-       movl    %edi, %r11d     // save mask
-       movl    (%rsi), %eax    // get value
-       movq    %rsi, %rdx      // put ptr where compare-and-swap expects it
-1:
-       movl    %r11d, %esi     // original mask
-       movl    %eax, %edi      // old value
-       orl     %eax, %esi      // new value
-       call    *%rcx           // %edi=old value,  %esi=new value. %rdx=ptr
-       jnz     1b
-       movl    %esi, %eax
+PLATFUNC_FUNCTION_START(OSAtomicOr32, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicOr32Barrier, up, 64, 2)
+       ATOMIC_ARITHMETIC(orl, ATOMIC_RET_NEW, ATOMIC_UP)
        ret
        
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32Barrier, mp, 64, 2)
+       ATOMIC_ARITHMETIC(orl, ATOMIC_RET_NEW, ATOMIC_MP)
+       ret
 
 // uint32_t OSAtomicXor32( uint32_t mask, uint32_t *value);
-DECLARE(_OSAtomicXor32)
-       movq    $(_COMM_PAGE_COMPARE_AND_SWAP32), %rcx
-       movl    %edi, %r11d     // save mask
-       movl    (%rsi), %eax    // get value
-       movq    %rsi, %rdx      // put ptr where compare-and-swap expects it
-1:
-       movl    %r11d, %esi     // original mask
-       movl    %eax, %edi      // old value
-       xorl    %eax, %esi      // new value
-       call    *%rcx           // %edi=old value,  %esi=new value. %rdx=ptr
-       jnz     1b
-       movl    %esi, %eax
+PLATFUNC_FUNCTION_START(OSAtomicXor32, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicXor32Barrier, up, 64, 2)
+       ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_NEW, ATOMIC_UP)
        ret
 
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32Barrier, mp, 64, 2)
+       ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_NEW, ATOMIC_MP)
+       ret
 
 // uint32_t OSAtomicAnd32Orig( uint32_t mask, uint32_t *value);
-DECLARE(_OSAtomicAnd32Orig)
-       movq    $(_COMM_PAGE_COMPARE_AND_SWAP32), %rcx
-       movl    %edi, %r11d     // save mask
-       movl    (%rsi), %eax    // get value
-       movq    %rsi, %rdx      // put ptr where compare-and-swap expects it
-1:
-       movl    %r11d, %esi     // original mask
-       movl    %eax, %edi      // old value
-       andl    %eax, %esi      // new value
-       call    *%rcx           // %edi=old value,  %esi=new value. %rdx=ptr
-       jnz     1b
-       movl    %edi, %eax
+PLATFUNC_FUNCTION_START(OSAtomicAnd32Orig, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicAnd32OrigBarrier, up, 64, 2)
+       ATOMIC_ARITHMETIC(andl, ATOMIC_RET_ORIG, ATOMIC_UP)
        ret
 
-
-// uint32_t OSAtomicOr32Orig( uint32_t mask, uint32_t *value);
-DECLARE(_OSAtomicOr32Orig)
-       movq    $(_COMM_PAGE_COMPARE_AND_SWAP32), %rcx
-       movl    %edi, %r11d     // save mask
-       movl    (%rsi), %eax    // get value
-       movq    %rsi, %rdx      // put ptr where compare-and-swap expects it
-1:
-       movl    %r11d, %esi     // original mask
-       movl    %eax, %edi      // old value
-       orl     %eax, %esi      // new value
-       call    *%rcx           // %edi=old value,  %esi=new value. %rdx=ptr
-       jnz     1b
-       movl    %edi, %eax
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32Orig, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAnd32OrigBarrier, mp, 64, 2)
+       ATOMIC_ARITHMETIC(andl, ATOMIC_RET_ORIG, ATOMIC_MP)
        ret
        
+// uint32_t OSAtomicOr32Orig( uint32_t mask, uint32_t *value);
+PLATFUNC_FUNCTION_START(OSAtomicOr32Orig, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicOr32OrigBarrier, up, 64, 2)
+       ATOMIC_ARITHMETIC(orl, ATOMIC_RET_ORIG, ATOMIC_UP)
+       ret
+
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32Orig, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicOr32OrigBarrier, mp, 64, 2)
+       ATOMIC_ARITHMETIC(orl, ATOMIC_RET_ORIG, ATOMIC_MP)
+       ret
 
 // uint32_t OSAtomicXor32Orig( uint32_t mask, uint32_t *value);
-DECLARE(_OSAtomicXor32Orig)
-       movq    $(_COMM_PAGE_COMPARE_AND_SWAP32), %rcx
-       movl    %edi, %r11d     // save mask
-       movl    (%rsi), %eax    // get value
-       movq    %rsi, %rdx      // put ptr where compare-and-swap expects it
-1:
-       movl    %r11d, %esi     // original mask
-       movl    %eax, %edi      // old value
-       xorl    %eax, %esi      // new value
-       call    *%rcx           // %edi=old value,  %esi=new value. %rdx=ptr
-       jnz     1b
-       movl    %edi, %eax
+PLATFUNC_FUNCTION_START(OSAtomicXor32Orig, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicXor32OrigBarrier, up, 64, 2)
+       ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_ORIG, ATOMIC_UP)
        ret
 
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32Orig, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicXor32OrigBarrier, mp, 64, 2)
+       ATOMIC_ARITHMETIC(xorl, ATOMIC_RET_ORIG, ATOMIC_MP)
+       ret
 
 // bool OSAtomicCompareAndSwap32( int32_t old, int32_t new, int32_t *value);
-DECLARE(_OSAtomicCompareAndSwapInt)
-DECLARE(_OSAtomicCompareAndSwap32)
-       movq    $(_COMM_PAGE_COMPARE_AND_SWAP32), %rcx
-       call    *%rcx           // %edi=old value,  %esi=new value. %rdx=ptr
+PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapInt, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapIntBarrier, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap32, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap32Barrier, up, 64, 2)
+       movl    %edi, %eax
+       xchg32  %esi, %rdx, ATOMIC_UP
        sete    %al
        movzbl  %al,%eax        // widen in case caller assumes we return an int
        ret
 
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapInt, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapIntBarrier, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap32, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap32Barrier, mp, 64, 2)
+       movl    %edi, %eax
+       xchg32  %esi, %rdx, ATOMIC_MP
+       sete    %al
+       movzbl  %al,%eax        // widen in case caller assumes we return an int
+       ret
 
 // bool OSAtomicCompareAndSwap64( int64_t old, int64_t new, int64_t *value);
-DECLARE(_OSAtomicCompareAndSwapPtr)
-DECLARE(_OSAtomicCompareAndSwapLong)
-DECLARE(_OSAtomicCompareAndSwap64)
-       movq    $(_COMM_PAGE_COMPARE_AND_SWAP64), %rcx
-       call    *%rcx           // %rdi=old value,  %rsi=new value. %rdx=ptr
+PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapPtr, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapPtrBarrier, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapLong, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwapLongBarrier, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap64, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicCompareAndSwap64Barrier, up, 64, 2)
+       mov     %rdi, %rax
+       xchg64  %rsi, %rdx, ATOMIC_UP
+       sete    %al
+       movzbl  %al,%eax        // widen in case caller assumes we return an int
+       ret
+       
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapPtr, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapPtrBarrier, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapLong, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwapLongBarrier, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap64, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicCompareAndSwap64Barrier, mp, 64, 2)
+       mov     %rdi, %rax
+       xchg64  %rsi, %rdx, ATOMIC_MP
        sete    %al
        movzbl  %al,%eax        // widen in case caller assumes we return an int
        ret
-
 
 // int32_t OSAtomicAdd32( int32_t amt, int32_t *value );
-DECLARE(_OSAtomicAdd32)
-       movq    $(_COMM_PAGE_ATOMIC_ADD32), %rcx
+PLATFUNC_FUNCTION_START(OSAtomicAdd32, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicAdd32Barrier, up, 64, 2)
        movl    %edi, %eax      // save amt to add
-       call    *%rcx
-       addl    %edi,%eax       // new value
+       xaddl   %edi, (%rsi)    // swap and add value, returns old value in %edi
+       addl    %edi, %eax      // add old value to amt as return value
        ret
 
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd32, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd32Barrier, mp, 64, 2)
+       movl    %edi, %eax      // save amt to add
+       lock                    // lock prefix breaks tabs ;)
+       xaddl   %edi, (%rsi)    // swap and add value, returns old value in %edi
+       addl    %edi, %eax      // add old value to amt as return value
+       ret
 
 // int64_t OSAtomicAdd64( int64_t amt, int64_t *value );
-DECLARE(_OSAtomicAdd64)
-       movq    $(_COMM_PAGE_ATOMIC_ADD64), %rcx
+PLATFUNC_FUNCTION_START(OSAtomicAdd64, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicAdd64Barrier, up, 64, 2)
        movq    %rdi, %rax      // save amt to add
-       call    *%rcx
-       addq    %rdi, %rax      // new value
+       xadd    %rdi, (%rsi)    // swap and add value, returns old value in %rsi
+       addq    %rdi, %rax      // add old value to amt as return value
        ret
 
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd64, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicAdd64Barrier, mp, 64, 2)
+       movq    %rdi, %rax      // save amt to add
+       lock
+       xadd    %rdi, (%rsi)    // swap and add value, returns old value in %rsi
+       addq    %rdi, %rax      // add old value to amt as return value
+       ret
 
 // bool OSAtomicTestAndSet( uint32_t n, void *value );
-DECLARE(_OSAtomicTestAndSet)
-       movq    $(_COMM_PAGE_BTS), %rax
-       xorl    $7, %edi        // bit position is numbered big endian
-       call    *%rax
-       setc    %al
-       movzbl  %al,%eax        // widen in case caller assumes we return an int
+PLATFUNC_FUNCTION_START(OSAtomicTestAndSet, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicTestAndSetBarrier, up, 64, 2)
+       ATOMIC_BIT_OP(btsl, ATOMIC_UP)
        ret
 
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndSet, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndSetBarrier, mp, 64, 2)
+       ATOMIC_BIT_OP(btsl, ATOMIC_MP)
+       ret
 
 // bool OSAtomicTestAndClear( uint32_t n, void *value );
-DECLARE(_OSAtomicTestAndClear)
-       movq    $(_COMM_PAGE_BTC), %rax
-       xorl    $7, %edi        // bit position is numbered big endian
-       call    *%rax
-       setc    %al
-       movzbl  %al,%eax        // widen in case caller assumes we return an int
+PLATFUNC_FUNCTION_START(OSAtomicTestAndClear, up, 64, 2)
+PLATFUNC_FUNCTION_START(OSAtomicTestAndClearBarrier, up, 64, 2)
+       ATOMIC_BIT_OP(btrl, ATOMIC_UP)
        ret
 
-// bool OSSpinLockTry( OSSpinLock *lock );
-       .align  2, 0x90
-       .globl  _OSSpinLockTry
-       .globl  __spin_lock_try
-_OSSpinLockTry:
-__spin_lock_try:
-       movq    $(_COMM_PAGE_SPINLOCK_TRY), %rax
-       jmp     *%rax
-
-
-// void OSSpinLockLock( OSSpinLock *lock );
-       .align  2, 0x90
-       .globl  _OSSpinLockLock
-       .globl  _spin_lock
-       .globl  __spin_lock
-_OSSpinLockLock:
-_spin_lock:
-__spin_lock:
-       movq    $(_COMM_PAGE_SPINLOCK_LOCK), %rax
-       jmp     *%rax
-
-
-// void OSSpinLockUnlock( OSSpinLock *lock );
-       .align  2, 0x90
-       .globl  _OSSpinLockUnlock
-       .globl  _spin_unlock
-       .globl  __spin_unlock
-_OSSpinLockUnlock:
-_spin_unlock:
-__spin_unlock:
-       movl    $0, (%rdi)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndClear, mp, 64, 2)
+PLATFUNC_FUNCTION_START_GENERIC(OSAtomicTestAndClearBarrier, mp, 64, 2)
+       ATOMIC_BIT_OP(btrl, ATOMIC_MP)
        ret
 
-
 // void OSMemoryBarrier( void );
        .align  2, 0x90
        .globl  _OSMemoryBarrier
 _OSMemoryBarrier:
-       movq    $(_COMM_PAGE_MEMORY_BARRIER), %rax
-       jmp     *%rax
-
+       mfence
+       ret
 
 /*
  *     typedef volatile struct {
@@ -255,9 +281,9 @@ _OSAtomicEnqueue:           // %rdi == list head, %rsi == new, %rdx == offset
        jnz     1b
        popq    %rbx
        ret
-       
-       
-/* void* OSAtomicDequeue( OSQueueHead *list, size_t offset); */
+
+
+       /* void* OSAtomicDequeue( OSQueueHead *list, size_t offset); */
        .align  2
        .globl  _OSAtomicDequeue
 _OSAtomicDequeue:              // %rdi == list head, %rsi == offset
@@ -276,3 +302,47 @@ _OSAtomicDequeue:          // %rdi == list head, %rsi == offset
 2:
        popq    %rbx
        ret                     // ptr to 1st element in Q still in %rax
+
+/*
+ *     typedef volatile struct {
+ *             void    *opaque1;  <-- ptr to first queue element or null
+ *             void    *opaque2;  <-- ptr to last queue element or null
+ *             int      opaque3;  <-- spinlock
+ *     } OSFifoQueueHead;
+ *
+ * void  OSAtomicFifoEnqueue( OSFifoQueueHead *list, void *new, size_t offset);
+ */
+       .align  2
+       .globl  _OSAtomicFifoEnqueue
+_OSAtomicFifoEnqueue:
+       pushq   %rbx
+       xorl    %ebx,%ebx       // clear "preemption pending" flag
+       movq    $(_COMM_PAGE_PFZ_ENQUEUE), %rcx
+       call    *%rcx
+       testl   %ebx,%ebx       // pending preemption?
+       jz      1f
+       call    _preempt        // call into the kernel to pfz_exit
+1:     
+       popq    %rbx
+       ret
+       
+       
+/* void* OSAtomicFifoDequeue( OSFifoQueueHead *list, size_t offset); */
+       .align  2
+       .globl  _OSAtomicFifoDequeue
+_OSAtomicFifoDequeue:
+       pushq   %rbx
+       xorl    %ebx,%ebx       // clear "preemption pending" flag
+       movq    %rsi,%rdx       // move offset to %rdx to be like the Enqueue case
+       movq    $(_COMM_PAGE_PFZ_DEQUEUE), %rcx
+       call    *%rcx
+       testl   %ebx,%ebx       // pending preemption?
+       jz      1f
+       call    _preempt        // call into the kernel to pfz_exit
+1:     
+       popq    %rbx
+       ret                     // ptr to 1st element in Q in %rax
+
+// Local Variables:
+// tab-width: 8
+// End: