/* * interlocked functions * * Copyright 1996 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "config.h" #include "wine/port.h" #include #ifdef __i386__ #ifdef __GNUC__ __ASM_GLOBAL_FUNC(interlocked_cmpxchg, "movl 12(%esp),%eax\n\t" "movl 8(%esp),%ecx\n\t" "movl 4(%esp),%edx\n\t" "lock; cmpxchgl %ecx,(%edx)\n\t" "ret") __ASM_GLOBAL_FUNC(interlocked_cmpxchg_ptr, "movl 12(%esp),%eax\n\t" "movl 8(%esp),%ecx\n\t" "movl 4(%esp),%edx\n\t" "lock; cmpxchgl %ecx,(%edx)\n\t" "ret") __ASM_GLOBAL_FUNC(interlocked_cmpxchg64, "push %ebx\n\t" __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") __ASM_CFI(".cfi_rel_offset %ebx,0\n\t") "push %esi\n\t" __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") __ASM_CFI(".cfi_rel_offset %esi,0\n\t") "movl 12(%esp),%esi\n\t" "movl 16(%esp),%ebx\n\t" "movl 20(%esp),%ecx\n\t" "movl 24(%esp),%eax\n\t" "movl 28(%esp),%edx\n\t" "lock; cmpxchg8b (%esi)\n\t" "pop %esi\n\t" __ASM_CFI(".cfi_same_value %esi\n\t") __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") "pop %ebx\n\t" __ASM_CFI(".cfi_same_value %ebx\n\t") __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") "ret") __ASM_GLOBAL_FUNC(interlocked_xchg, "movl 8(%esp),%eax\n\t" "movl 4(%esp),%edx\n\t" "lock; xchgl %eax,(%edx)\n\t" "ret") __ASM_GLOBAL_FUNC(interlocked_xchg_ptr, "movl 8(%esp),%eax\n\t" "movl 4(%esp),%edx\n\t" "lock; xchgl %eax,(%edx)\n\t" "ret") __ASM_GLOBAL_FUNC(interlocked_xchg_add, "movl 8(%esp),%eax\n\t" "movl 4(%esp),%edx\n\t" "lock; xaddl %eax,(%edx)\n\t" "ret") #elif defined(_MSC_VER) __declspec(naked) int interlocked_cmpxchg( int *dest, int xchg, int compare ) { __asm mov eax, 12[esp]; __asm mov ecx, 8[esp]; __asm mov edx, 4[esp]; __asm lock cmpxchg [edx], ecx; __asm ret; } __declspec(naked) void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare ) { __asm mov eax, 12[esp]; __asm mov ecx, 8[esp]; __asm mov edx, 4[esp]; __asm lock cmpxchg [edx], ecx; __asm ret; } __declspec(naked) __int64 interlocked_cmpxchg64( __int64 *dest, __int64 xchg, __int64 compare) { __asm push ebx; __asm push esi; __asm mov esi, 12[esp]; __asm mov ebx, 16[esp]; __asm mov ecx, 20[esp]; __asm mov eax, 24[esp]; __asm mov edx, 28[esp]; __asm lock cmpxchg8b [esi]; __asm pop esi; __asm pop ebx; __asm ret; } __declspec(naked) int interlocked_xchg( int *dest, int val ) { __asm mov eax, 8[esp]; __asm mov edx, 4[esp]; __asm lock xchg [edx], eax; __asm ret; } __declspec(naked) void *interlocked_xchg_ptr( void **dest, void *val ) { __asm mov eax, 8[esp]; __asm mov edx, 4[esp]; __asm lock xchg [edx], eax; __asm ret; } __declspec(naked) int interlocked_xchg_add( int *dest, int incr ) { __asm mov eax, 8[esp]; __asm mov edx, 4[esp]; __asm lock xadd [edx], eax; __asm ret; } #else # error You must implement the interlocked* functions for your compiler #endif #elif defined(__x86_64__) #ifdef __GNUC__ __ASM_GLOBAL_FUNC(interlocked_cmpxchg, "mov %edx, %eax\n\t" "lock cmpxchgl %esi,(%rdi)\n\t" "ret") __ASM_GLOBAL_FUNC(interlocked_cmpxchg_ptr, "mov %rdx, %rax\n\t" "lock cmpxchgq %rsi,(%rdi)\n\t" "ret") __ASM_GLOBAL_FUNC(interlocked_cmpxchg64, "mov %rdx, %rax\n\t" "lock cmpxchgq %rsi,(%rdi)\n\t" "ret") __ASM_GLOBAL_FUNC(interlocked_xchg, "mov %esi, %eax\n\t" "lock xchgl %eax, (%rdi)\n\t" "ret") __ASM_GLOBAL_FUNC(interlocked_xchg_ptr, "mov %rsi, %rax\n\t" "lock xchgq %rax,(%rdi)\n\t" "ret") __ASM_GLOBAL_FUNC(interlocked_xchg_add, "mov %esi, %eax\n\t" "lock xaddl %eax, (%rdi)\n\t" "ret") #else # error You must implement the interlocked* functions for your compiler #endif #elif defined(__powerpc__) void* interlocked_cmpxchg_ptr( void **dest, void* xchg, void* compare) { void *ret = 0; void *scratch; __asm__ __volatile__( "0: lwarx %0,0,%2\n" " xor. %1,%4,%0\n" " bne 1f\n" " stwcx. %3,0,%2\n" " bne- 0b\n" " isync\n" "1: " : "=&r"(ret), "=&r"(scratch) : "r"(dest), "r"(xchg), "r"(compare) : "cr0","memory"); return ret; } __int64 interlocked_cmpxchg64( __int64 *dest, __int64 xchg, __int64 compare) { /* FIXME: add code */ assert(0); } int interlocked_cmpxchg( int *dest, int xchg, int compare) { int ret = 0; int scratch; __asm__ __volatile__( "0: lwarx %0,0,%2\n" " xor. %1,%4,%0\n" " bne 1f\n" " stwcx. %3,0,%2\n" " bne- 0b\n" " isync\n" "1: " : "=&r"(ret), "=&r"(scratch) : "r"(dest), "r"(xchg), "r"(compare) : "cr0","memory","r0"); return ret; } int interlocked_xchg_add( int *dest, int incr ) { int ret = 0; int zero = 0; __asm__ __volatile__( "0: lwarx %0, %3, %1\n" " add %0, %2, %0\n" " stwcx. %0, %3, %1\n" " bne- 0b\n" " isync\n" : "=&r" (ret) : "r"(dest), "r"(incr), "r"(zero) : "cr0", "memory", "r0" ); return ret-incr; } int interlocked_xchg( int* dest, int val ) { int ret = 0; __asm__ __volatile__( "0: lwarx %0,0,%1\n" " stwcx. %2,0,%1\n" " bne- 0b\n" " isync\n" : "=&r"(ret) : "r"(dest), "r"(val) : "cr0","memory","r0"); return ret; } void* interlocked_xchg_ptr( void** dest, void* val ) { void *ret = NULL; __asm__ __volatile__( "0: lwarx %0,0,%1\n" " stwcx. %2,0,%1\n" " bne- 0b\n" " isync\n" : "=&r"(ret) : "r"(dest), "r"(val) : "cr0","memory","r0"); return ret; } #elif defined(__sparc__) && defined(__sun__) /* * As the earlier Sparc processors lack necessary atomic instructions, * I'm simply falling back to the library-provided _lwp_mutex routines * to ensure mutual exclusion in a way appropriate for the current * architecture. * * FIXME: If we have the compare-and-swap instruction (Sparc v9 and above) * we could use this to speed up the Interlocked operations ... */ #include static lwp_mutex_t interlocked_mutex = DEFAULTMUTEX; int interlocked_cmpxchg( int *dest, int xchg, int compare ) { _lwp_mutex_lock( &interlocked_mutex ); if (*dest == compare) *dest = xchg; else compare = *dest; _lwp_mutex_unlock( &interlocked_mutex ); return compare; } void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare ) { _lwp_mutex_lock( &interlocked_mutex ); if (*dest == compare) *dest = xchg; else compare = *dest; _lwp_mutex_unlock( &interlocked_mutex ); return compare; } __int64 interlocked_cmpxchg64( __int64 *dest, __int64 xchg, __int64 compare ) { _lwp_mutex_lock( &interlocked_mutex ); if (*dest == compare) *dest = xchg; else compare = *dest; _lwp_mutex_unlock( &interlocked_mutex ); return compare; } int interlocked_xchg( int *dest, int val ) { int retv; _lwp_mutex_lock( &interlocked_mutex ); retv = *dest; *dest = val; _lwp_mutex_unlock( &interlocked_mutex ); return retv; } void *interlocked_xchg_ptr( void **dest, void *val ) { void *retv; _lwp_mutex_lock( &interlocked_mutex ); retv = *dest; *dest = val; _lwp_mutex_unlock( &interlocked_mutex ); return retv; } int interlocked_xchg_add( int *dest, int incr ) { int retv; _lwp_mutex_lock( &interlocked_mutex ); retv = *dest; *dest += incr; _lwp_mutex_unlock( &interlocked_mutex ); return retv; } #elif defined(__ALPHA__) && defined(__GNUC__) __ASM_GLOBAL_FUNC(interlocked_cmpxchg, "L0cmpxchg:\n\t" "ldl_l $0,0($16)\n\t" "cmpeq $0,$18,$1\n\t" "beq $1,L1cmpxchg\n\t" "mov $17,$0\n\t" "stl_c $0,0($16)\n\t" "beq $0,L0cmpxchg\n\t" "mov $18,$0\n" "L1cmpxchg:\n\t" "mb") __ASM_GLOBAL_FUNC(interlocked_cmpxchg_ptr, "L0cmpxchg_ptr:\n\t" "ldq_l $0,0($16)\n\t" "cmpeq $0,$18,$1\n\t" "beq $1,L1cmpxchg_ptr\n\t" "mov $17,$0\n\t" "stq_c $0,0($16)\n\t" "beq $0,L0cmpxchg_ptr\n\t" "mov $18,$0\n" "L1cmpxchg_ptr:\n\t" "mb") __int64 interlocked_cmpxchg64(__int64 *dest, __int64 xchg, __int64 compare) { /* FIXME: add code */ assert(0); } __ASM_GLOBAL_FUNC(interlocked_xchg, "L0xchg:\n\t" "ldl_l $0,0($16)\n\t" "mov $17,$1\n\t" "stl_c $1,0($16)\n\t" "beq $1,L0xchg\n\t" "mb") __ASM_GLOBAL_FUNC(interlocked_xchg_ptr, "L0xchg_ptr:\n\t" "ldq_l $0,0($16)\n\t" "mov $17,$1\n\t" "stq_c $1,0($16)\n\t" "beq $1,L0xchg_ptr\n\t" "mb") __ASM_GLOBAL_FUNC(interlocked_xchg_add, "L0xchg_add:\n\t" "ldl_l $0,0($16)\n\t" "addl $0,$17,$1\n\t" "stl_c $1,0($16)\n\t" "beq $1,L0xchg_add\n\t" "mb") #else # error You must implement the interlocked* functions for your CPU #endif