b07265954c83cdfca6885a5fa467d3645912e72a
[mono.git] / mono / utils / atomic.h
1 /*
2  * atomic.h:  Atomic operations
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  * Copyright 2012 Xamarin Inc
9  */
10
11 #ifndef _WAPI_ATOMIC_H_
12 #define _WAPI_ATOMIC_H_
13
14 #include "config.h"
15 #include <glib.h>
16
17 #ifdef ENABLE_EXTENSION_MODULE
18 #include "../../../mono-extensions/mono/utils/atomic.h"
19 #endif
20
21 /* On Windows, we always use the functions provided by the Windows API. */
22 #if defined(__WIN32__) || defined(_WIN32)
23
24 #include <windows.h>
25
26 /* mingw is missing InterlockedCompareExchange64 () from winbase.h */
27 #if HAVE_DECL_INTERLOCKEDCOMPAREEXCHANGE64==0
28 static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
29 {
30         return __sync_val_compare_and_swap (dest, comp, exch);
31 }
32 #endif
33
34 /* Prefer GCC atomic ops if the target supports it (see configure.in). */
35 #elif defined(USE_GCC_ATOMIC_OPS)
36
37 static inline gint32 InterlockedCompareExchange(volatile gint32 *dest,
38                                                 gint32 exch, gint32 comp)
39 {
40         return __sync_val_compare_and_swap (dest, comp, exch);
41 }
42
43 static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp)
44 {
45         return __sync_val_compare_and_swap (dest, comp, exch);
46 }
47
48 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
49 {
50         return __sync_add_and_fetch (dest, add);
51 }
52
53 static inline gint32 InterlockedIncrement(volatile gint32 *val)
54 {
55         return __sync_add_and_fetch (val, 1);
56 }
57
58 static inline gint32 InterlockedDecrement(volatile gint32 *val)
59 {
60         return __sync_add_and_fetch (val, -1);
61 }
62
63 static inline gint32 InterlockedExchange(volatile gint32 *val, gint32 new_val)
64 {
65         gint32 old_val;
66         do {
67                 old_val = *val;
68         } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
69         return old_val;
70 }
71
72 static inline gpointer InterlockedExchangePointer(volatile gpointer *val,
73                                                   gpointer new_val)
74 {
75         gpointer old_val;
76         do {
77                 old_val = *val;
78         } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
79         return old_val;
80 }
81
82 static inline gint32 InterlockedExchangeAdd(volatile gint32 *val, gint32 add)
83 {
84         return __sync_fetch_and_add (val, add);
85 }
86
87 #if defined (TARGET_OSX)
88 #define BROKEN_64BIT_ATOMICS_INTRINSIC 1
89 #endif
90
91 #if !defined (BROKEN_64BIT_ATOMICS_INTRINSIC)
92
93 static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
94 {
95         return __sync_val_compare_and_swap (dest, comp, exch);
96 }
97
98 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
99 {
100         return __sync_add_and_fetch (dest, add);
101 }
102
103 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
104 {
105         return __sync_add_and_fetch (val, 1);
106 }
107
108 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
109 {
110         return __sync_sub_and_fetch (val, 1);
111 }
112
113 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *val, gint64 add)
114 {
115         return __sync_fetch_and_add (val, add);
116 }
117
118 #else
119
120 /* Implement 64-bit cmpxchg by hand or emulate it. */
121 extern gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp);
122
123 /* Implement all other 64-bit atomics in terms of a specialized CAS
124  * in this case, since chances are that the other 64-bit atomic
125  * intrinsics are broken too.
126  */
127
128 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add)
129 {
130         gint64 old_val;
131         do {
132                 old_val = *dest;
133         } while (InterlockedCompareExchange64 (dest, old_val + add, old_val) != old_val);
134         return old_val;
135 }
136
137 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
138 {
139         gint64 get, set;
140         do {
141                 get = *val;
142                 set = get + 1;
143         } while (InterlockedCompareExchange64 (val, set, get) != set);
144         return set;
145 }
146
147 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
148 {
149         gint64 get, set;
150         do {
151                 get = *val;
152                 set = get - 1;
153         } while (InterlockedCompareExchange64 (val, set, get) != set);
154         return set;
155 }
156
157 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
158 {
159         gint64 get, set;
160         do {
161                 get = *dest;
162                 set = get + add;
163         } while (InterlockedCompareExchange64 (dest, set, get) != set);
164         return set;
165 }
166
167 static inline gint64 InterlockedRead64(volatile gint64 *src)
168 {
169         return InterlockedCompareExchange64 (src, 0, 0);
170 }
171
172 #endif
173
174 /* We always implement this in terms of a 64-bit cmpxchg since
175  * GCC doesn't have an intrisic to model it anyway. */
176 static inline gint64 InterlockedExchange64(volatile gint64 *val, gint64 new_val)
177 {
178         gint64 old_val;
179         do {
180                 old_val = *val;
181         } while (InterlockedCompareExchange64 (val, new_val, old_val) != old_val);
182         return old_val;
183 }
184
185 static inline void InterlockedWrite64(volatile gint64 *dst, gint64 val)
186 {
187         /* Nothing useful from GCC at all, so fall back to CAS. */
188         InterlockedExchange64 (dst, val);
189 }
190
191 #elif (defined(sparc) || defined (__sparc__)) && defined(__GNUC__)
192
193 G_GNUC_UNUSED 
194 static inline gint32 InterlockedCompareExchange(volatile gint32 *_dest, gint32 _exch, gint32 _comp)
195 {
196        register volatile gint32 *dest asm("g1") = _dest;
197        register gint32 comp asm("o4") = _comp;
198        register gint32 exch asm("o5") = _exch;
199
200        __asm__ __volatile__(
201                /* cas [%%g1], %%o4, %%o5 */
202                ".word 0xdbe0500c"
203                : "=r" (exch)
204                : "0" (exch), "r" (dest), "r" (comp)
205                : "memory");
206
207        return exch;
208 }
209
210 G_GNUC_UNUSED 
211 static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *_dest, gpointer _exch, gpointer _comp)
212 {
213        register volatile gpointer *dest asm("g1") = _dest;
214        register gpointer comp asm("o4") = _comp;
215        register gpointer exch asm("o5") = _exch;
216
217        __asm__ __volatile__(
218 #ifdef SPARCV9
219                /* casx [%%g1], %%o4, %%o5 */
220                ".word 0xdbf0500c"
221 #else
222                /* cas [%%g1], %%o4, %%o5 */
223                ".word 0xdbe0500c"
224 #endif
225                : "=r" (exch)
226                : "0" (exch), "r" (dest), "r" (comp)
227                : "memory");
228
229        return exch;
230 }
231
232 G_GNUC_UNUSED 
233 static inline gint32 InterlockedIncrement(volatile gint32 *_dest)
234 {
235        register volatile gint32 *dest asm("g1") = _dest;
236        register gint32 tmp asm("o4");
237        register gint32 ret asm("o5");
238
239        __asm__ __volatile__(
240                "1:     ld      [%%g1], %%o4\n\t"
241                "       add     %%o4, 1, %%o5\n\t"
242                /*      cas     [%%g1], %%o4, %%o5 */
243                "       .word   0xdbe0500c\n\t"
244                "       cmp     %%o4, %%o5\n\t"
245                "       bne     1b\n\t"
246                "        add    %%o5, 1, %%o5"
247                : "=&r" (tmp), "=&r" (ret)
248                : "r" (dest)
249                : "memory", "cc");
250
251         return ret;
252 }
253
254 G_GNUC_UNUSED 
255 static inline gint32 InterlockedDecrement(volatile gint32 *_dest)
256 {
257        register volatile gint32 *dest asm("g1") = _dest;
258        register gint32 tmp asm("o4");
259        register gint32 ret asm("o5");
260
261        __asm__ __volatile__(
262                "1:     ld      [%%g1], %%o4\n\t"
263                "       sub     %%o4, 1, %%o5\n\t"
264                /*      cas     [%%g1], %%o4, %%o5 */
265                "       .word   0xdbe0500c\n\t"
266                "       cmp     %%o4, %%o5\n\t"
267                "       bne     1b\n\t"
268                "        sub    %%o5, 1, %%o5"
269                : "=&r" (tmp), "=&r" (ret)
270                : "r" (dest)
271                : "memory", "cc");
272
273         return ret;
274 }
275
276 G_GNUC_UNUSED
277 static inline gint32 InterlockedExchange(volatile gint32 *_dest, gint32 exch)
278 {
279        register volatile gint32 *dest asm("g1") = _dest;
280        register gint32 tmp asm("o4");
281        register gint32 ret asm("o5");
282
283        __asm__ __volatile__(
284                "1:     ld      [%%g1], %%o4\n\t"
285                "       mov     %3, %%o5\n\t"
286                /*      cas     [%%g1], %%o4, %%o5 */
287                "       .word   0xdbe0500c\n\t"
288                "       cmp     %%o4, %%o5\n\t"
289                "       bne     1b\n\t"
290                "        nop"
291                : "=&r" (tmp), "=&r" (ret)
292                : "r" (dest), "r" (exch)
293                : "memory", "cc");
294
295         return ret;
296 }
297
298 G_GNUC_UNUSED
299 static inline gpointer InterlockedExchangePointer(volatile gpointer *_dest, gpointer exch)
300 {
301        register volatile gpointer *dest asm("g1") = _dest;
302        register gpointer tmp asm("o4");
303        register gpointer ret asm("o5");
304
305        __asm__ __volatile__(
306 #ifdef SPARCV9
307                "1:     ldx     [%%g1], %%o4\n\t"
308 #else
309                "1:     ld      [%%g1], %%o4\n\t"
310 #endif
311                "       mov     %3, %%o5\n\t"
312 #ifdef SPARCV9
313                /*      casx    [%%g1], %%o4, %%o5 */
314                "       .word   0xdbf0500c\n\t"
315 #else
316                /*      cas     [%%g1], %%o4, %%o5 */
317                "       .word   0xdbe0500c\n\t"
318 #endif
319                "       cmp     %%o4, %%o5\n\t"
320                "       bne     1b\n\t"
321                "        nop"
322                : "=&r" (tmp), "=&r" (ret)
323                : "r" (dest), "r" (exch)
324                : "memory", "cc");
325
326         return ret;
327 }
328
329 G_GNUC_UNUSED
330 static inline gint32 InterlockedExchangeAdd(volatile gint32 *_dest, gint32 add)
331 {
332        register volatile gint32 *dest asm("g1") = _dest;
333        register gint32 tmp asm("o4");
334        register gint32 ret asm("o5");
335
336        __asm__ __volatile__(
337                "1:     ld      [%%g1], %%o4\n\t"
338                "       add     %%o4, %3, %%o5\n\t"
339                /*      cas     [%%g1], %%o4, %%o5 */
340                "       .word   0xdbe0500c\n\t"
341                "       cmp     %%o4, %%o5\n\t"
342                "       bne     1b\n\t"
343                "        add    %%o5, %3, %%o5"
344                : "=&r" (tmp), "=&r" (ret)
345                : "r" (dest), "r" (add)
346                : "memory", "cc");
347
348         return ret;
349 }
350
351 #elif defined(__ia64__)
352
353 #ifdef __INTEL_COMPILER
354 #include <ia64intrin.h>
355 #endif
356
357 static inline gint32 InterlockedCompareExchange(gint32 volatile *dest,
358                                                 gint32 exch, gint32 comp)
359 {
360         gint32 old;
361         guint64 real_comp;
362
363 #ifdef __INTEL_COMPILER
364         old = _InterlockedCompareExchange (dest, exch, comp);
365 #else
366         /* cmpxchg4 zero extends the value read from memory */
367         real_comp = (guint64)(guint32)comp;
368         asm volatile ("mov ar.ccv = %2 ;;\n\t"
369                                   "cmpxchg4.acq %0 = [%1], %3, ar.ccv\n\t"
370                                   : "=r" (old) : "r" (dest), "r" (real_comp), "r" (exch));
371 #endif
372
373         return(old);
374 }
375
376 static inline gpointer InterlockedCompareExchangePointer(gpointer volatile *dest,
377                                                 gpointer exch, gpointer comp)
378 {
379         gpointer old;
380
381 #ifdef __INTEL_COMPILER
382         old = _InterlockedCompareExchangePointer (dest, exch, comp);
383 #else
384         asm volatile ("mov ar.ccv = %2 ;;\n\t"
385                                   "cmpxchg8.acq %0 = [%1], %3, ar.ccv\n\t"
386                                   : "=r" (old) : "r" (dest), "r" (comp), "r" (exch));
387 #endif
388
389         return(old);
390 }
391
392 static inline gint32 InterlockedIncrement(gint32 volatile *val)
393 {
394 #ifdef __INTEL_COMPILER
395         return _InterlockedIncrement (val);
396 #else
397         gint32 old;
398
399         do {
400                 old = *val;
401         } while (InterlockedCompareExchange (val, old + 1, old) != old);
402
403         return old + 1;
404 #endif
405 }
406
407 static inline gint32 InterlockedDecrement(gint32 volatile *val)
408 {
409 #ifdef __INTEL_COMPILER
410         return _InterlockedDecrement (val);
411 #else
412         gint32 old;
413
414         do {
415                 old = *val;
416         } while (InterlockedCompareExchange (val, old - 1, old) != old);
417
418         return old - 1;
419 #endif
420 }
421
422 static inline gint32 InterlockedExchange(gint32 volatile *dest, gint32 new_val)
423 {
424 #ifdef __INTEL_COMPILER
425         return _InterlockedExchange (dest, new_val);
426 #else
427         gint32 res;
428
429         do {
430                 res = *dest;
431         } while (InterlockedCompareExchange (dest, new_val, res) != res);
432
433         return res;
434 #endif
435 }
436
437 static inline gpointer InterlockedExchangePointer(gpointer volatile *dest, gpointer new_val)
438 {
439 #ifdef __INTEL_COMPILER
440         return (gpointer)_InterlockedExchange64 ((gint64*)dest, (gint64)new_val);
441 #else
442         gpointer res;
443
444         do {
445                 res = *dest;
446         } while (InterlockedCompareExchangePointer (dest, new_val, res) != res);
447
448         return res;
449 #endif
450 }
451
452 static inline gint32 InterlockedExchangeAdd(gint32 volatile *val, gint32 add)
453 {
454         gint32 old;
455
456 #ifdef __INTEL_COMPILER
457         old = _InterlockedExchangeAdd (val, add);
458 #else
459         do {
460                 old = *val;
461         } while (InterlockedCompareExchange (val, old + add, old) != old);
462
463         return old;
464 #endif
465 }
466
467 #else
468
469 #define WAPI_NO_ATOMIC_ASM
470
471 extern gint32 InterlockedCompareExchange(volatile gint32 *dest, gint32 exch, gint32 comp);
472 extern gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp);
473 extern gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp);
474 extern gint32 InterlockedAdd(volatile gint32 *dest, gint32 add);
475 extern gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add);
476 extern gint32 InterlockedIncrement(volatile gint32 *dest);
477 extern gint64 InterlockedIncrement64(volatile gint64 *dest);
478 extern gint32 InterlockedDecrement(volatile gint32 *dest);
479 extern gint64 InterlockedDecrement64(volatile gint64 *dest);
480 extern gint32 InterlockedExchange(volatile gint32 *dest, gint32 exch);
481 extern gint64 InterlockedExchange64(volatile gint64 *dest, gint64 exch);
482 extern gpointer InterlockedExchangePointer(volatile gpointer *dest, gpointer exch);
483 extern gint32 InterlockedExchangeAdd(volatile gint32 *dest, gint32 add);
484 extern gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add);
485
486 #endif
487
488 #endif /* _WAPI_ATOMIC_H_ */