Adjust various FP register values for hard float in mini-arm.h.
[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 #ifndef WIN32_LEAN_AND_MEAN
25 #define WIN32_LEAN_AND_MEAN
26 #endif
27 #include <windows.h>
28 #include <mono/utils/mono-membar.h>
29
30 /* mingw is missing InterlockedCompareExchange64 () from winbase.h */
31 #if HAVE_DECL_INTERLOCKEDCOMPAREEXCHANGE64==0
32 static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
33 {
34         return __sync_val_compare_and_swap (dest, comp, exch);
35 }
36 #endif
37
38 /* mingw is missing InterlockedExchange64 () from winbase.h */
39 #if HAVE_DECL_INTERLOCKEDEXCHANGE64==0
40 static inline gint64 InterlockedExchange64(volatile gint64 *val, gint64 new_val)
41 {
42         gint64 old_val;
43         do {
44                 old_val = *val;
45         } while (InterlockedCompareExchange64 (val, new_val, old_val) != old_val);
46         return old_val;
47 }
48 #endif
49
50 /* mingw is missing InterlockedIncrement64 () from winbase.h */
51 #if HAVE_DECL_INTERLOCKEDINCREMENT64==0
52 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
53 {
54         return __sync_add_and_fetch (val, 1);
55 }
56 #endif
57
58 /* mingw is missing InterlockedDecrement64 () from winbase.h */
59 #if HAVE_DECL_INTERLOCKEDDECREMENT64==0
60 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
61 {
62         return __sync_sub_and_fetch (val, 1);
63 }
64 #endif
65
66 /* mingw is missing InterlockedAdd () from winbase.h */
67 #if HAVE_DECL_INTERLOCKEDADD==0
68 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
69 {
70         return __sync_add_and_fetch (dest, add);
71 }
72 #endif
73
74 /* mingw is missing InterlockedAdd64 () from winbase.h */
75 #if HAVE_DECL_INTERLOCKEDADD64==0
76 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
77 {
78         return __sync_add_and_fetch (dest, add);
79 }
80 #endif
81
82 /* And now for some dirty hacks... The Windows API doesn't
83  * provide any useful primitives for this (other than getting
84  * into architecture-specific madness), so use CAS. */
85
86 static inline gint32 InterlockedRead(volatile gint32 *src)
87 {
88         return InterlockedCompareExchange (src, 0, 0);
89 }
90
91 static inline gint64 InterlockedRead64(volatile gint64 *src)
92 {
93         return InterlockedCompareExchange64 (src, 0, 0);
94 }
95
96 static inline gpointer InterlockedReadPointer(volatile gpointer *src)
97 {
98         return InterlockedCompareExchangePointer (src, NULL, NULL);
99 }
100
101 static inline void InterlockedWrite(volatile gint32 *dst, gint32 val)
102 {
103         InterlockedExchange (dst, val);
104 }
105
106 static inline void InterlockedWrite64(volatile gint64 *dst, gint64 val)
107 {
108         InterlockedExchange64 (dst, val);
109 }
110
111 static inline void InterlockedWritePointer(volatile gpointer *dst, gpointer val)
112 {
113         InterlockedExchangePointer (dst, val);
114 }
115
116 /* We can't even use CAS for these, so write them out
117  * explicitly according to x86(_64) semantics... */
118
119 static inline gint8 InterlockedRead8(volatile gint8 *src)
120 {
121         return *src;
122 }
123
124 static inline gint16 InterlockedRead16(volatile gint16 *src)
125 {
126         return *src;
127 }
128
129 static inline void InterlockedWrite8(volatile gint8 *dst, gint8 val)
130 {
131         *dst = val;
132         mono_memory_barrier ();
133 }
134
135 static inline void InterlockedWrite16(volatile gint16 *dst, gint16 val)
136 {
137         *dst = val;
138         mono_memory_barrier ();
139 }
140
141 /* Prefer GCC atomic ops if the target supports it (see configure.in). */
142 #elif defined(USE_GCC_ATOMIC_OPS)
143
144 static inline gint32 InterlockedCompareExchange(volatile gint32 *dest,
145                                                 gint32 exch, gint32 comp)
146 {
147         return __sync_val_compare_and_swap (dest, comp, exch);
148 }
149
150 static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp)
151 {
152         return __sync_val_compare_and_swap (dest, comp, exch);
153 }
154
155 static inline gint32 InterlockedAdd(volatile gint32 *dest, gint32 add)
156 {
157         return __sync_add_and_fetch (dest, add);
158 }
159
160 static inline gint32 InterlockedIncrement(volatile gint32 *val)
161 {
162         return __sync_add_and_fetch (val, 1);
163 }
164
165 static inline gint32 InterlockedDecrement(volatile gint32 *val)
166 {
167         return __sync_sub_and_fetch (val, 1);
168 }
169
170 static inline gint32 InterlockedExchange(volatile gint32 *val, gint32 new_val)
171 {
172         gint32 old_val;
173         do {
174                 old_val = *val;
175         } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
176         return old_val;
177 }
178
179 static inline gpointer InterlockedExchangePointer(volatile gpointer *val,
180                                                   gpointer new_val)
181 {
182         gpointer old_val;
183         do {
184                 old_val = *val;
185         } while (__sync_val_compare_and_swap (val, old_val, new_val) != old_val);
186         return old_val;
187 }
188
189 static inline gint32 InterlockedExchangeAdd(volatile gint32 *val, gint32 add)
190 {
191         return __sync_fetch_and_add (val, add);
192 }
193
194 static inline gint8 InterlockedRead8(volatile gint8 *src)
195 {
196         /* Kind of a hack, but GCC doesn't give us anything better, and it's
197          * certainly not as bad as using a CAS loop. */
198         return __sync_fetch_and_add (src, 0);
199 }
200
201 static inline gint16 InterlockedRead16(volatile gint16 *src)
202 {
203         return __sync_fetch_and_add (src, 0);
204 }
205
206 static inline gint32 InterlockedRead(volatile gint32 *src)
207 {
208         return __sync_fetch_and_add (src, 0);
209 }
210
211 static inline void InterlockedWrite8(volatile gint8 *dst, gint8 val)
212 {
213         /* Nothing useful from GCC at all, so fall back to CAS. */
214         gint8 old_val;
215         do {
216                 old_val = *dst;
217         } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
218 }
219
220 static inline void InterlockedWrite16(volatile gint16 *dst, gint16 val)
221 {
222         gint16 old_val;
223         do {
224                 old_val = *dst;
225         } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
226 }
227
228 static inline void InterlockedWrite(volatile gint32 *dst, gint32 val)
229 {
230         /* Nothing useful from GCC at all, so fall back to CAS. */
231         gint32 old_val;
232         do {
233                 old_val = *dst;
234         } while (__sync_val_compare_and_swap (dst, old_val, val) != old_val);
235 }
236
237 #if defined (TARGET_OSX) || defined (__arm__) || (defined (__mips__) && !defined (__mips64)) || (defined (__powerpc__) && !defined (__powerpc64__)) || (defined (__sparc__) && !defined (__arch64__))
238 #define BROKEN_64BIT_ATOMICS_INTRINSIC 1
239 #endif
240
241 #if !defined (BROKEN_64BIT_ATOMICS_INTRINSIC)
242
243 static inline gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp)
244 {
245         return __sync_val_compare_and_swap (dest, comp, exch);
246 }
247
248 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
249 {
250         return __sync_add_and_fetch (dest, add);
251 }
252
253 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
254 {
255         return __sync_add_and_fetch (val, 1);
256 }
257
258 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
259 {
260         return __sync_sub_and_fetch (val, 1);
261 }
262
263 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *val, gint64 add)
264 {
265         return __sync_fetch_and_add (val, add);
266 }
267
268 static inline gint64 InterlockedRead64(volatile gint64 *src)
269 {
270         /* Kind of a hack, but GCC doesn't give us anything better. */
271         return __sync_fetch_and_add (src, 0);
272 }
273
274 #else
275
276 /* Implement 64-bit cmpxchg by hand or emulate it. */
277 extern gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp);
278
279 /* Implement all other 64-bit atomics in terms of a specialized CAS
280  * in this case, since chances are that the other 64-bit atomic
281  * intrinsics are broken too.
282  */
283
284 static inline gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add)
285 {
286         gint64 old_val;
287         do {
288                 old_val = *dest;
289         } while (InterlockedCompareExchange64 (dest, old_val + add, old_val) != old_val);
290         return old_val;
291 }
292
293 static inline gint64 InterlockedIncrement64(volatile gint64 *val)
294 {
295         gint64 get, set;
296         do {
297                 get = *val;
298                 set = get + 1;
299         } while (InterlockedCompareExchange64 (val, set, get) != get);
300         return set;
301 }
302
303 static inline gint64 InterlockedDecrement64(volatile gint64 *val)
304 {
305         gint64 get, set;
306         do {
307                 get = *val;
308                 set = get - 1;
309         } while (InterlockedCompareExchange64 (val, set, get) != get);
310         return set;
311 }
312
313 static inline gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add)
314 {
315         gint64 get, set;
316         do {
317                 get = *dest;
318                 set = get + add;
319         } while (InterlockedCompareExchange64 (dest, set, get) != get);
320         return set;
321 }
322
323 static inline gint64 InterlockedRead64(volatile gint64 *src)
324 {
325         return InterlockedCompareExchange64 (src, 0, 0);
326 }
327
328 #endif
329
330 static inline gpointer InterlockedReadPointer(volatile gpointer *src)
331 {
332         return InterlockedCompareExchangePointer (src, NULL, NULL);
333 }
334
335 static inline void InterlockedWritePointer(volatile gpointer *dst, gpointer val)
336 {
337         InterlockedExchangePointer (dst, val);
338 }
339
340 /* We always implement this in terms of a 64-bit cmpxchg since
341  * GCC doesn't have an intrisic to model it anyway. */
342 static inline gint64 InterlockedExchange64(volatile gint64 *val, gint64 new_val)
343 {
344         gint64 old_val;
345         do {
346                 old_val = *val;
347         } while (InterlockedCompareExchange64 (val, new_val, old_val) != old_val);
348         return old_val;
349 }
350
351 static inline void InterlockedWrite64(volatile gint64 *dst, gint64 val)
352 {
353         /* Nothing useful from GCC at all, so fall back to CAS. */
354         InterlockedExchange64 (dst, val);
355 }
356
357 #elif defined(__ia64__)
358
359 #ifdef __INTEL_COMPILER
360 #include <ia64intrin.h>
361 #endif
362
363 static inline gint32 InterlockedCompareExchange(gint32 volatile *dest,
364                                                 gint32 exch, gint32 comp)
365 {
366         gint32 old;
367         guint64 real_comp;
368
369 #ifdef __INTEL_COMPILER
370         old = _InterlockedCompareExchange (dest, exch, comp);
371 #else
372         /* cmpxchg4 zero extends the value read from memory */
373         real_comp = (guint64)(guint32)comp;
374         asm volatile ("mov ar.ccv = %2 ;;\n\t"
375                                   "cmpxchg4.acq %0 = [%1], %3, ar.ccv\n\t"
376                                   : "=r" (old) : "r" (dest), "r" (real_comp), "r" (exch));
377 #endif
378
379         return(old);
380 }
381
382 static inline gpointer InterlockedCompareExchangePointer(gpointer volatile *dest,
383                                                 gpointer exch, gpointer comp)
384 {
385         gpointer old;
386
387 #ifdef __INTEL_COMPILER
388         old = _InterlockedCompareExchangePointer (dest, exch, comp);
389 #else
390         asm volatile ("mov ar.ccv = %2 ;;\n\t"
391                                   "cmpxchg8.acq %0 = [%1], %3, ar.ccv\n\t"
392                                   : "=r" (old) : "r" (dest), "r" (comp), "r" (exch));
393 #endif
394
395         return(old);
396 }
397
398 static inline gint32 InterlockedIncrement(gint32 volatile *val)
399 {
400 #ifdef __INTEL_COMPILER
401         return _InterlockedIncrement (val);
402 #else
403         gint32 old;
404
405         do {
406                 old = *val;
407         } while (InterlockedCompareExchange (val, old + 1, old) != old);
408
409         return old + 1;
410 #endif
411 }
412
413 static inline gint32 InterlockedDecrement(gint32 volatile *val)
414 {
415 #ifdef __INTEL_COMPILER
416         return _InterlockedDecrement (val);
417 #else
418         gint32 old;
419
420         do {
421                 old = *val;
422         } while (InterlockedCompareExchange (val, old - 1, old) != old);
423
424         return old - 1;
425 #endif
426 }
427
428 static inline gint32 InterlockedExchange(gint32 volatile *dest, gint32 new_val)
429 {
430 #ifdef __INTEL_COMPILER
431         return _InterlockedExchange (dest, new_val);
432 #else
433         gint32 res;
434
435         do {
436                 res = *dest;
437         } while (InterlockedCompareExchange (dest, new_val, res) != res);
438
439         return res;
440 #endif
441 }
442
443 static inline gpointer InterlockedExchangePointer(gpointer volatile *dest, gpointer new_val)
444 {
445 #ifdef __INTEL_COMPILER
446         return (gpointer)_InterlockedExchange64 ((gint64*)dest, (gint64)new_val);
447 #else
448         gpointer res;
449
450         do {
451                 res = *dest;
452         } while (InterlockedCompareExchangePointer (dest, new_val, res) != res);
453
454         return res;
455 #endif
456 }
457
458 static inline gint32 InterlockedExchangeAdd(gint32 volatile *val, gint32 add)
459 {
460         gint32 old;
461
462 #ifdef __INTEL_COMPILER
463         old = _InterlockedExchangeAdd (val, add);
464 #else
465         do {
466                 old = *val;
467         } while (InterlockedCompareExchange (val, old + add, old) != old);
468
469         return old;
470 #endif
471 }
472
473 #else
474
475 #define WAPI_NO_ATOMIC_ASM
476
477 extern gint32 InterlockedCompareExchange(volatile gint32 *dest, gint32 exch, gint32 comp);
478 extern gint64 InterlockedCompareExchange64(volatile gint64 *dest, gint64 exch, gint64 comp);
479 extern gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp);
480 extern gint32 InterlockedAdd(volatile gint32 *dest, gint32 add);
481 extern gint64 InterlockedAdd64(volatile gint64 *dest, gint64 add);
482 extern gint32 InterlockedIncrement(volatile gint32 *dest);
483 extern gint64 InterlockedIncrement64(volatile gint64 *dest);
484 extern gint32 InterlockedDecrement(volatile gint32 *dest);
485 extern gint64 InterlockedDecrement64(volatile gint64 *dest);
486 extern gint32 InterlockedExchange(volatile gint32 *dest, gint32 exch);
487 extern gint64 InterlockedExchange64(volatile gint64 *dest, gint64 exch);
488 extern gpointer InterlockedExchangePointer(volatile gpointer *dest, gpointer exch);
489 extern gint32 InterlockedExchangeAdd(volatile gint32 *dest, gint32 add);
490 extern gint64 InterlockedExchangeAdd64(volatile gint64 *dest, gint64 add);
491 extern gint8 InterlockedRead8(volatile gint8 *src);
492 extern gint16 InterlockedRead16(volatile gint16 *src);
493 extern gint32 InterlockedRead(volatile gint32 *src);
494 extern gint64 InterlockedRead64(volatile gint64 *src);
495 extern gpointer InterlockedReadPointer(volatile gpointer *src);
496 extern void InterlockedWrite8(volatile gint8 *dst, gint8 val);
497 extern void InterlockedWrite16(volatile gint16 *dst, gint16 val);
498 extern void InterlockedWrite(volatile gint32 *dst, gint32 val);
499 extern void InterlockedWrite64(volatile gint64 *dst, gint64 val);
500 extern void InterlockedWritePointer(volatile gpointer *dst, gpointer val);
501
502 #endif
503
504 #endif /* _WAPI_ATOMIC_H_ */