2 * atomic.h: Atomic operations
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
8 * Copyright 2012 Xamarin Inc
11 #ifndef _WAPI_ATOMIC_H_
12 #define _WAPI_ATOMIC_H_
18 The current Nexus 7 arm-v7a fails with:
19 F/MonoDroid( 1568): shared runtime initialization error: Cannot load library: reloc_library[1285]: 37 cannot locate '__sync_val_compare_and_swap_8'
21 Apple targets have historically being problematic, xcode 4.6 would miscompile the intrinsic.
24 #if defined (__arm__) && defined (HAVE_ARMV7)
25 #define HAVE_64BIT_CMPXCHG_FALLBACK /* See atomic.c in this directory. */
28 /* On Windows, we always use the functions provided by the Windows API. */
29 #if defined(__WIN32__) || defined(_WIN32)
31 #ifndef WIN32_LEAN_AND_MEAN
32 #define WIN32_LEAN_AND_MEAN
35 #include <mono/utils/mono-membar.h>
37 /* mingw is missing InterlockedCompareExchange64 () from winbase.h */
38 #if HAVE_DECL_INTERLOCKEDCOMPAREEXCHANGE64==0
39 static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
41 return __sync_val_compare_and_swap (dest, comp, exch);
45 /* mingw is missing InterlockedExchange64 () from winbase.h */
46 #if HAVE_DECL_INTERLOCKEDEXCHANGE64==0
47 static inline gint64 InterlockedExchange64(volatile gint64 *val, gint64 new_val)
52 } while (InterlockedCompareExchange64 (val, new_val, old_val) != old_val);
57 /* mingw is missing InterlockedIncrement64 () from winbase.h */
58 #if HAVE_DECL_INTERLOCKEDINCREMENT64==0
59 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
61 return __sync_add_and_fetch (val, 1);
65 /* mingw is missing InterlockedDecrement64 () from winbase.h */
66 #if HAVE_DECL_INTERLOCKEDDECREMENT64==0
67 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
69 return __sync_sub_and_fetch (val, 1);
73 /* mingw is missing InterlockedAdd () from winbase.h */
74 #if HAVE_DECL_INTERLOCKEDADD==0
75 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
77 return __sync_add_and_fetch (dest, add);
81 /* mingw is missing InterlockedAdd64 () from winbase.h */
82 #if HAVE_DECL_INTERLOCKEDADD64==0
83 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
85 return __sync_add_and_fetch (dest, add);
89 #if defined(_MSC_VER) && !defined(InterlockedAdd)
90 /* MSVC before 2013 only defines InterlockedAdd* for the Itanium architecture */
91 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
93 return InterlockedExchangeAdd (dest, add) + add;
97 #if defined(_MSC_VER) && !defined(InterlockedAdd64)
98 #if defined(InterlockedExchangeAdd64)
99 /* This may be defined only on amd64 */
100 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
102 return InterlockedExchangeAdd64 (dest, add) + add;
105 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
111 } while (prev_value != InterlockedCompareExchange64(dest, prev_value + add, prev_value));
113 return prev_value + add;
119 #define TO_INTERLOCKED_ARGP(ptr) ((volatile LONG*)(ptr))
121 #define TO_INTERLOCKED_ARGP(ptr) (ptr)
124 /* And now for some dirty hacks... The Windows API doesn't
125 * provide any useful primitives for this (other than getting
126 * into architecture-specific madness), so use CAS. */
128 static inline gint32 InterlockedRead(volatile gint32 *src)
130 return InterlockedCompareExchange (TO_INTERLOCKED_ARGP (src), 0, 0);
133 static inline gint64 InterlockedRead64(volatile gint64 *src)
135 return InterlockedCompareExchange64 (src, 0, 0);
138 static inline gpointer InterlockedReadPointer(volatile gpointer *src)
140 return InterlockedCompareExchangePointer (src, NULL, NULL);
143 static inline void InterlockedWrite(volatile gint32 *dst, gint32 val)
145 InterlockedExchange (TO_INTERLOCKED_ARGP (dst), val);
148 static inline void InterlockedWrite64(volatile gint64 *dst, gint64 val)
150 InterlockedExchange64 (dst, val);
153 static inline void InterlockedWritePointer(volatile gpointer *dst, gpointer val)
155 InterlockedExchangePointer (dst, val);
158 /* We can't even use CAS for these, so write them out
159 * explicitly according to x86(_64) semantics... */
161 static inline gint8 InterlockedRead8(volatile gint8 *src)
166 static inline gint16 InterlockedRead16(volatile gint16 *src)
171 static inline void InterlockedWrite8(volatile gint8 *dst, gint8 val)
174 mono_memory_barrier ();
177 static inline void InterlockedWrite16(volatile gint16 *dst, gint16 val)
180 mono_memory_barrier ();
183 /* Prefer GCC atomic ops if the target supports it (see configure.ac). */
184 #elif defined(USE_GCC_ATOMIC_OPS)
186 static inline gint32 InterlockedCompareExchange(volatile gint32 *dest,
187 gint32 exch, gint32 comp)
189 return __sync_val_compare_and_swap (dest, comp, exch);
192 static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp)
194 return __sync_val_compare_and_swap (dest, comp, exch);
197 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
199 return __sync_add_and_fetch (dest, add);
202 static inline gint32 InterlockedIncrement(volatile gint32 *val)
204 return __sync_add_and_fetch (val, 1);
207 static inline gint32 InterlockedDecrement(volatile gint32 *val)
209 return __sync_sub_and_fetch (val, 1);
212 static inline gint32 InterlockedExchange(volatile gint32 *val, gint32 new_val)
217 } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
221 static inline gpointer InterlockedExchangePointer(volatile gpointer *val,
227 } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
231 static inline gint32 InterlockedExchangeAdd(volatile gint32 *val, gint32 add)
233 return __sync_fetch_and_add (val, add);
236 static inline gint8 InterlockedRead8(volatile gint8 *src)
238 /* Kind of a hack, but GCC doesn't give us anything better, and it's
239 * certainly not as bad as using a CAS loop. */
240 return __sync_fetch_and_add (src, 0);
243 static inline gint16 InterlockedRead16(volatile gint16 *src)
245 return __sync_fetch_and_add (src, 0);
248 static inline gint32 InterlockedRead(volatile gint32 *src)
250 return __sync_fetch_and_add (src, 0);
253 static inline void InterlockedWrite8(volatile gint8 *dst, gint8 val)
255 /* Nothing useful from GCC at all, so fall back to CAS. */
259 } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
262 static inline void InterlockedWrite16(volatile gint16 *dst, gint16 val)
267 } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
270 static inline void InterlockedWrite(volatile gint32 *dst, gint32 val)
272 /* Nothing useful from GCC at all, so fall back to CAS. */
276 } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
279 #if defined (TARGET_OSX) || defined (__arm__) || (defined (__mips__) && !defined (__mips64)) || (defined (__powerpc__) && !defined (__powerpc64__)) || (defined (__sparc__) && !defined (__arch64__))
280 #define BROKEN_64BIT_ATOMICS_INTRINSIC 1
283 #if !defined (BROKEN_64BIT_ATOMICS_INTRINSIC)
285 static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
287 return __sync_val_compare_and_swap (dest, comp, exch);
290 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
292 return __sync_add_and_fetch (dest, add);
295 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
297 return __sync_add_and_fetch (val, 1);
300 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
302 return __sync_sub_and_fetch (val, 1);
305 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *val, gint64 add)
307 return __sync_fetch_and_add (val, add);
310 static inline gint64 InterlockedRead64(volatile gint64 *src)
312 /* Kind of a hack, but GCC doesn't give us anything better. */
313 return __sync_fetch_and_add (src, 0);
318 /* Implement 64-bit cmpxchg by hand or emulate it. */
319 extern gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp);
321 /* Implement all other 64-bit atomics in terms of a specialized CAS
322 * in this case, since chances are that the other 64-bit atomic
323 * intrinsics are broken too.
326 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add)
331 } while (InterlockedCompareExchange64 (dest, old_val + add, old_val) != old_val);
335 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
341 } while (InterlockedCompareExchange64 (val, set, get) != get);
345 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
351 } while (InterlockedCompareExchange64 (val, set, get) != get);
355 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
361 } while (InterlockedCompareExchange64 (dest, set, get) != get);
365 static inline gint64 InterlockedRead64(volatile gint64 *src)
367 return InterlockedCompareExchange64 (src, 0, 0);
372 static inline gpointer InterlockedReadPointer(volatile gpointer *src)
374 return InterlockedCompareExchangePointer (src, NULL, NULL);
377 static inline void InterlockedWritePointer(volatile gpointer *dst, gpointer val)
379 InterlockedExchangePointer (dst, val);
382 /* We always implement this in terms of a 64-bit cmpxchg since
383 * GCC doesn't have an intrisic to model it anyway. */
384 static inline gint64 InterlockedExchange64(volatile gint64 *val, gint64 new_val)
389 } while (InterlockedCompareExchange64 (val, new_val, old_val) != old_val);
393 static inline void InterlockedWrite64(volatile gint64 *dst, gint64 val)
395 /* Nothing useful from GCC at all, so fall back to CAS. */
396 InterlockedExchange64 (dst, val);
399 #elif defined(__ia64__)
401 #ifdef __INTEL_COMPILER
402 #include <ia64intrin.h>
405 static inline gint32 InterlockedCompareExchange(gint32 volatile *dest,
406 gint32 exch, gint32 comp)
411 #ifdef __INTEL_COMPILER
412 old = _InterlockedCompareExchange (dest, exch, comp);
414 /* cmpxchg4 zero extends the value read from memory */
415 real_comp = (guint64)(guint32)comp;
416 asm volatile ("mov ar.ccv = %2 ;;\n\t"
417 "cmpxchg4.acq %0 = [%1], %3, ar.ccv\n\t"
418 : "=r" (old) : "r" (dest), "r" (real_comp), "r" (exch));
424 static inline gpointer InterlockedCompareExchangePointer(gpointer volatile *dest,
425 gpointer exch, gpointer comp)
429 #ifdef __INTEL_COMPILER
430 old = _InterlockedCompareExchangePointer (dest, exch, comp);
432 asm volatile ("mov ar.ccv = %2 ;;\n\t"
433 "cmpxchg8.acq %0 = [%1], %3, ar.ccv\n\t"
434 : "=r" (old) : "r" (dest), "r" (comp), "r" (exch));
440 static inline gint32 InterlockedIncrement(gint32 volatile *val)
442 #ifdef __INTEL_COMPILER
443 return _InterlockedIncrement (val);
449 } while (InterlockedCompareExchange (val, old + 1, old) != old);
455 static inline gint32 InterlockedDecrement(gint32 volatile *val)
457 #ifdef __INTEL_COMPILER
458 return _InterlockedDecrement (val);
464 } while (InterlockedCompareExchange (val, old - 1, old) != old);
470 static inline gint32 InterlockedExchange(gint32 volatile *dest, gint32 new_val)
472 #ifdef __INTEL_COMPILER
473 return _InterlockedExchange (dest, new_val);
479 } while (InterlockedCompareExchange (dest, new_val, res) != res);
485 static inline gpointer InterlockedExchangePointer(gpointer volatile *dest, gpointer new_val)
487 #ifdef __INTEL_COMPILER
488 return (gpointer)_InterlockedExchange64 ((gint64*)dest, (gint64)new_val);
494 } while (InterlockedCompareExchangePointer (dest, new_val, res) != res);
500 static inline gint32 InterlockedExchangeAdd(gint32 volatile *val, gint32 add)
504 #ifdef __INTEL_COMPILER
505 old = _InterlockedExchangeAdd (val, add);
509 } while (InterlockedCompareExchange (val, old + add, old) != old);
517 #define WAPI_NO_ATOMIC_ASM
519 extern gint32 InterlockedCompareExchange(volatile gint32 *dest, gint32 exch, gint32 comp);
520 extern gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp);
521 extern gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp);
522 extern gint32 InterlockedAdd(volatile gint32 *dest, gint32 add);
523 extern gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add);
524 extern gint32 InterlockedIncrement(volatile gint32 *dest);
525 extern gint64 InterlockedIncrement64(volatile gint64 *dest);
526 extern gint32 InterlockedDecrement(volatile gint32 *dest);
527 extern gint64 InterlockedDecrement64(volatile gint64 *dest);
528 extern gint32 InterlockedExchange(volatile gint32 *dest, gint32 exch);
529 extern gint64 InterlockedExchange64(volatile gint64 *dest, gint64 exch);
530 extern gpointer InterlockedExchangePointer(volatile gpointer *dest, gpointer exch);
531 extern gint32 InterlockedExchangeAdd(volatile gint32 *dest, gint32 add);
532 extern gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add);
533 extern gint8 InterlockedRead8(volatile gint8 *src);
534 extern gint16 InterlockedRead16(volatile gint16 *src);
535 extern gint32 InterlockedRead(volatile gint32 *src);
536 extern gint64 InterlockedRead64(volatile gint64 *src);
537 extern gpointer InterlockedReadPointer(volatile gpointer *src);
538 extern void InterlockedWrite8(volatile gint8 *dst, gint8 val);
539 extern void InterlockedWrite16(volatile gint16 *dst, gint16 val);
540 extern void InterlockedWrite(volatile gint32 *dst, gint32 val);
541 extern void InterlockedWrite64(volatile gint64 *dst, gint64 val);
542 extern void InterlockedWritePointer(volatile gpointer *dst, gpointer val);
546 #endif /* _WAPI_ATOMIC_H_ */