mesa.library: Fix inverted atomic decrement result on m68k
[AROS.git] / workbench / libs / mesa / src / gallium / auxiliary / util / u_atomic.h
bloba6a3189a0f397eb882438bb914f43e658fb49d76
1 /**
2 * Many similar implementations exist. See for example libwsbm
3 * or the linux kernel include/atomic.h
5 * No copyright claimed on this file.
7 */
9 #ifndef U_ATOMIC_H
10 #define U_ATOMIC_H
12 #include "pipe/p_compiler.h"
13 #include "pipe/p_defines.h"
15 /* Favor OS-provided implementations.
17 * Where no OS-provided implementation is available, fall back to
18 * locally coded assembly, compiler intrinsic or ultimately a
19 * mutex-based implementation.
21 #if (defined(PIPE_SUBSYSTEM_WINDOWS_DISPLAY) || \
22 defined(PIPE_SUBSYSTEM_WINDOWS_MINIPORT))
23 #define PIPE_ATOMIC_OS_UNLOCKED
24 #elif defined(PIPE_OS_SOLARIS)
25 #define PIPE_ATOMIC_OS_SOLARIS
26 #elif defined(PIPE_CC_MSVC)
27 #define PIPE_ATOMIC_MSVC_INTRINSIC
28 #elif (defined(PIPE_CC_MSVC) && defined(PIPE_ARCH_X86))
29 #define PIPE_ATOMIC_ASM_MSVC_X86
30 #elif (defined(PIPE_CC_GCC) && defined(PIPE_ARCH_X86))
31 #define PIPE_ATOMIC_ASM_GCC_X86
32 #elif (defined(PIPE_CC_GCC) && defined(PIPE_ARCH_ARM))
33 #define PIPE_ATOMIC_ASM_GCC_ARM
34 #elif (defined(PIPE_CC_GCC) && defined(PIPE_ARCH_X86_64))
35 #define PIPE_ATOMIC_ASM_GCC_X86_64
36 #elif defined(PIPE_OS_AROS) && defined(PIPE_ARCH_M68K)
37 #define PIPE_ATOMIC_OS_AROS_CPU_M68K
38 #elif defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION >= 401)
39 #define PIPE_ATOMIC_GCC_INTRINSIC
40 #else
41 #error "Unsupported platform"
42 #endif
45 #if defined(PIPE_ATOMIC_ASM_GCC_X86_64)
46 #define PIPE_ATOMIC "GCC x86_64 assembly"
48 #ifdef __cplusplus
49 extern "C" {
50 #endif
52 #define p_atomic_set(_v, _i) (*(_v) = (_i))
53 #define p_atomic_read(_v) (*(_v))
55 static INLINE boolean
56 p_atomic_dec_zero(int32_t *v)
58 unsigned char c;
60 __asm__ __volatile__("lock; decl %0; sete %1":"+m"(*v), "=qm"(c)
61 ::"memory");
63 return c != 0;
66 static INLINE void
67 p_atomic_inc(int32_t *v)
69 __asm__ __volatile__("lock; incl %0":"+m"(*v));
72 static INLINE void
73 p_atomic_dec(int32_t *v)
75 __asm__ __volatile__("lock; decl %0":"+m"(*v));
78 static INLINE int32_t
79 p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new)
81 return __sync_val_compare_and_swap(v, old, _new);
84 #ifdef __cplusplus
86 #endif
88 #endif /* PIPE_ATOMIC_ASM_GCC_X86_64 */
91 #if defined(PIPE_ATOMIC_ASM_GCC_X86)
93 #define PIPE_ATOMIC "GCC x86 assembly"
95 #ifdef __cplusplus
96 extern "C" {
97 #endif
99 #define p_atomic_set(_v, _i) (*(_v) = (_i))
100 #define p_atomic_read(_v) (*(_v))
102 static INLINE boolean
103 p_atomic_dec_zero(int32_t *v)
105 unsigned char c;
107 __asm__ __volatile__("lock; decl %0; sete %1":"+m"(*v), "=qm"(c)
108 ::"memory");
110 return c != 0;
113 static INLINE void
114 p_atomic_inc(int32_t *v)
116 __asm__ __volatile__("lock; incl %0":"+m"(*v));
119 static INLINE void
120 p_atomic_dec(int32_t *v)
122 __asm__ __volatile__("lock; decl %0":"+m"(*v));
125 static INLINE int32_t
126 p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new)
128 return __sync_val_compare_and_swap(v, old, _new);
131 #ifdef __cplusplus
133 #endif
135 #endif
137 #if defined(PIPE_ATOMIC_ASM_GCC_ARM)
139 #define PIPE_ATOMIC "GCC ARM assembly"
141 #ifdef __cplusplus
142 extern "C" {
143 #endif
145 #define p_atomic_set(_v, _i) (*(_v) = (_i))
146 #define p_atomic_read(_v) (*(_v))
148 static INLINE boolean
149 p_atomic_dec_zero(int32_t *v)
151 unsigned long temp;
152 int result;
153 unsigned long cc;
154 __asm__ __volatile__("\n1: ldrex %0, [%3]; subs %0, %0, #1; moveq %2, #1; movne %2, #0; strex %1, %0, [%3]; teq %1, #0; bne 1b"
155 :"=&r"(result), "=&r"(temp), "=&r"(cc)
156 :"r"(v)
157 :"cc");
158 return cc;
161 static INLINE void
162 p_atomic_inc(int32_t *v)
164 unsigned long temp;
165 int result;
166 __asm__ __volatile__("\n1: ldrex %0, [%2]; add %0, %0, #1; strex %1, %0, [%2]; teq %1, #0; bne 1b"
167 :"=&r"(result), "=&r"(temp)
168 :"r"(v)
169 :"cc");
172 static INLINE void
173 p_atomic_dec(int32_t *v)
175 unsigned long temp;
176 int result;
177 __asm__ __volatile__("\n1: ldrex %0, [%2]; sub %0, %0, #1; strex %1, %0, [%2]; teq %1, #0; bne 1b"
178 :"=&r"(result), "=&r"(temp)
179 :"r"(v)
180 :"cc");
183 static INLINE int32_t
184 p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new)
186 int32_t oldval;
187 unsigned long temp;
188 __asm__ __volatile__("\n1: ldrex %0,[%2]; teq %0, %3; strexeq %1, %4, [%2]; teq %1, #0; bne 1b"
189 :"=&r"(oldval), "=&r"(temp)
190 :"r"(v), "Ir"(old), "r"(_new)
191 :"cc");
192 return oldval;
195 #ifdef __cplusplus
197 #endif
199 #endif
202 /* Implementation using GCC-provided synchronization intrinsics
204 #if defined(PIPE_ATOMIC_GCC_INTRINSIC)
206 #define PIPE_ATOMIC "GCC Sync Intrinsics"
208 #ifdef __cplusplus
209 extern "C" {
210 #endif
212 #define p_atomic_set(_v, _i) (*(_v) = (_i))
213 #define p_atomic_read(_v) (*(_v))
215 static INLINE boolean
216 p_atomic_dec_zero(int32_t *v)
218 return (__sync_sub_and_fetch(v, 1) == 0);
221 static INLINE void
222 p_atomic_inc(int32_t *v)
224 (void) __sync_add_and_fetch(v, 1);
227 static INLINE void
228 p_atomic_dec(int32_t *v)
230 (void) __sync_sub_and_fetch(v, 1);
233 static INLINE int32_t
234 p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new)
236 return __sync_val_compare_and_swap(v, old, _new);
239 #ifdef __cplusplus
241 #endif
243 #endif
247 /* Unlocked version for single threaded environments, such as some
248 * windows kernel modules.
250 #if defined(PIPE_ATOMIC_OS_UNLOCKED)
252 #define PIPE_ATOMIC "Unlocked"
254 #define p_atomic_set(_v, _i) (*(_v) = (_i))
255 #define p_atomic_read(_v) (*(_v))
256 #define p_atomic_dec_zero(_v) ((boolean) --(*(_v)))
257 #define p_atomic_inc(_v) ((void) (*(_v))++)
258 #define p_atomic_dec(_v) ((void) (*(_v))--)
259 #define p_atomic_cmpxchg(_v, old, _new) (*(_v) == old ? *(_v) = (_new) : *(_v))
261 #endif
264 /* Locally coded assembly for MSVC on x86:
266 #if defined(PIPE_ATOMIC_ASM_MSVC_X86)
268 #define PIPE_ATOMIC "MSVC x86 assembly"
270 #ifdef __cplusplus
271 extern "C" {
272 #endif
274 #define p_atomic_set(_v, _i) (*(_v) = (_i))
275 #define p_atomic_read(_v) (*(_v))
277 static INLINE boolean
278 p_atomic_dec_zero(int32_t *v)
280 unsigned char c;
282 __asm {
283 mov eax, [v]
284 lock dec dword ptr [eax]
285 sete byte ptr [c]
288 return c != 0;
291 static INLINE void
292 p_atomic_inc(int32_t *v)
294 __asm {
295 mov eax, [v]
296 lock inc dword ptr [eax]
300 static INLINE void
301 p_atomic_dec(int32_t *v)
303 __asm {
304 mov eax, [v]
305 lock dec dword ptr [eax]
309 static INLINE int32_t
310 p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new)
312 int32_t orig;
314 __asm {
315 mov ecx, [v]
316 mov eax, [old]
317 mov edx, [_new]
318 lock cmpxchg [ecx], edx
319 mov [orig], eax
322 return orig;
325 #ifdef __cplusplus
327 #endif
329 #endif
332 #if defined(PIPE_ATOMIC_MSVC_INTRINSIC)
334 #define PIPE_ATOMIC "MSVC Intrinsics"
336 #include <intrin.h>
338 #pragma intrinsic(_InterlockedIncrement)
339 #pragma intrinsic(_InterlockedDecrement)
340 #pragma intrinsic(_InterlockedCompareExchange)
342 #ifdef __cplusplus
343 extern "C" {
344 #endif
346 #define p_atomic_set(_v, _i) (*(_v) = (_i))
347 #define p_atomic_read(_v) (*(_v))
349 static INLINE boolean
350 p_atomic_dec_zero(int32_t *v)
352 return _InterlockedDecrement((long *)v) == 0;
355 static INLINE void
356 p_atomic_inc(int32_t *v)
358 _InterlockedIncrement((long *)v);
361 static INLINE void
362 p_atomic_dec(int32_t *v)
364 _InterlockedDecrement((long *)v);
367 static INLINE int32_t
368 p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new)
370 return _InterlockedCompareExchange((long *)v, _new, old);
373 #ifdef __cplusplus
375 #endif
377 #endif
379 #if defined(PIPE_ATOMIC_OS_SOLARIS)
381 #define PIPE_ATOMIC "Solaris OS atomic functions"
383 #include <atomic.h>
385 #ifdef __cplusplus
386 extern "C" {
387 #endif
389 #define p_atomic_set(_v, _i) (*(_v) = (_i))
390 #define p_atomic_read(_v) (*(_v))
392 static INLINE boolean
393 p_atomic_dec_zero(int32_t *v)
395 uint32_t n = atomic_dec_32_nv((uint32_t *) v);
397 return n != 0;
400 #define p_atomic_inc(_v) atomic_inc_32((uint32_t *) _v)
401 #define p_atomic_dec(_v) atomic_dec_32((uint32_t *) _v)
403 #define p_atomic_cmpxchg(_v, _old, _new) \
404 atomic_cas_32( (uint32_t *) _v, (uint32_t) _old, (uint32_t) _new)
406 #ifdef __cplusplus
408 #endif
410 #endif
413 #if defined(PIPE_ATOMIC_OS_AROS_CPU_M68K)
415 #define PIPE_ATOMIC "AROS OS atomic functions"
417 #include <aros/atomic.h>
419 #ifdef __cplusplus
420 extern "C" {
421 #endif
423 #define p_atomic_set(_v, _i) (*(_v) = (_i))
424 #define p_atomic_read(_v) (*(_v))
426 static INLINE boolean
427 p_atomic_dec_zero(int32_t *v)
429 boolean n;
431 /* FIXME: AROS needs an atomic decrement and return... */
432 Disable();
433 AROS_ATOMIC_DEC(*(LONG *)v);
434 n = (*v == 0) ? TRUE : FALSE;
435 Enable();
437 return n;
440 #define p_atomic_inc(_v) AROS_ATOMIC_INC(*(LONG *)_v)
441 #define p_atomic_dec(_v) AROS_ATOMIC_DEC(*(LONG *)_v)
443 static INLINE int32_t
444 p_atomic_cmpxchg(int32_t *v, int32_t o, int32_t n)
446 int32_t ret;
448 /* FIXME: AROS needs an atomic cmpxchg, using CAS.
449 * However we can't do this if:
450 * a) We are on a 68000 or
451 * b) The 'v' points to Chip RAM (no r/m/w possible)
453 * Settle for Disable()/Enable() for now.
455 Disable();
456 if (*v == o)
457 *v = (n);
458 ret = *v;
459 Enable();
461 return ret;
464 #ifdef __cplusplus
466 #endif
468 #endif
471 #ifndef PIPE_ATOMIC
472 #error "No pipe_atomic implementation selected"
473 #endif
477 #endif /* U_ATOMIC_H */