2004-01-29 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / io-layer / atomic.h
1 /*
2  * atomic.h:  Atomic operations
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #ifndef _WAPI_ATOMIC_H_
11 #define _WAPI_ATOMIC_H_
12
13 #include <glib.h>
14
15 #include "mono/io-layer/wapi.h"
16
17 #ifdef __i386__
18 #define WAPI_ATOMIC_ASM
19
20 /*
21  * NB: The *Pointer() functions here assume that
22  * sizeof(pointer)==sizeof(gint32)
23  *
24  * NB2: These asm functions assume 486+ (some of the opcodes dont
25  * exist on 386).  If this becomes an issue, we can get configure to
26  * fall back to the non-atomic C versions of these calls.
27  */
28
29 static inline gint32 InterlockedCompareExchange(volatile gint32 *dest,
30                                                 gint32 exch, gint32 comp)
31 {
32         gint32 old;
33
34         __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
35                               : "=m" (*dest), "=a" (old)
36                               : "r" (exch), "m" (*dest), "a" (comp));   
37         return(old);
38 }
39
40 static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp)
41 {
42         gpointer old;
43
44         __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
45                               : "=m" (*dest), "=a" (old)
46                               : "r" (exch), "m" (*dest), "a" (comp));   
47         return(old);
48 }
49
50 static inline gint32 InterlockedIncrement(volatile gint32 *val)
51 {
52         gint32 tmp;
53         
54         __asm__ __volatile__ ("lock; xaddl %0, %1"
55                               : "=r" (tmp), "=m" (*val)
56                               : "0" (1), "m" (*val));
57
58         return(tmp+1);
59 }
60
61 static inline gint32 InterlockedDecrement(volatile gint32 *val)
62 {
63         gint32 tmp;
64         
65         __asm__ __volatile__ ("lock; xaddl %0, %1"
66                               : "=r" (tmp), "=m" (*val)
67                               : "0" (-1), "m" (*val));
68
69         return(tmp-1);
70 }
71
72 /*
73  * See
74  * http://msdn.microsoft.com/library/en-us/dnmag00/html/win320700.asp?frame=true
75  * for the reasons for using cmpxchg and a loop here.
76  *
77  * That url is no longer valid, but it's still in the google cache at the
78  * moment: http://www.google.com/search?q=cache:http://msdn.microsoft.com/library/en-us/dnmag00/html/win320700.asp?frame=true
79  *
80  * For the time being, http://msdn.microsoft.com/msdnmag/issues/0700/Win32/
81  * might work.  Bet it will change soon enough though.
82  */
83 static inline gint32 InterlockedExchange(volatile gint32 *val, gint32 new_val)
84 {
85         gint32 ret;
86         
87         __asm__ __volatile__ ("1:; lock; cmpxchgl %2, %0; jne 1b"
88                               : "=m" (*val), "=a" (ret)
89                               : "r" (new_val), "m" (*val), "a" (*val));
90
91         return(ret);
92 }
93
94 static inline gpointer InterlockedExchangePointer(volatile gpointer *val,
95                                                   gpointer new_val)
96 {
97         gpointer ret;
98         
99         __asm__ __volatile__ ("1:; lock; cmpxchgl %2, %0; jne 1b"
100                               : "=m" (*val), "=a" (ret)
101                               : "r" (new_val), "m" (*val), "a" (*val));
102
103         return(ret);
104 }
105
106 static inline gint32 InterlockedExchangeAdd(volatile gint32 *val, gint32 add)
107 {
108         gint32 ret;
109         
110         __asm__ __volatile__ ("lock; xaddl %0, %1"
111                               : "=r" (ret), "=m" (*val)
112                               : "0" (add), "m" (*val));
113         
114         return(ret);
115 }
116
117 #elif defined(sparc) || defined (__sparc__)
118 #define WAPI_ATOMIC_ASM
119
120 #ifdef __GNUC__
121 #define BEGIN_SPIN(tmp,lock) \
122 __asm__ __volatile__("1:        ldstub [%1],%0\n\t"  \
123                              "          cmp %0, 0\n\t" \
124                              "          bne 1b\n\t" \
125                              "          nop" \
126                              : "=&r" (tmp) \
127                              : "r" (&lock) \
128                              : "memory"); 
129
130 #define END_SPIN(lock) \
131 __asm__ __volatile__("stb       %%g0, [%0]"  \
132                       : /* no outputs */ \
133                       : "r" (&lock)\
134                       : "memory");
135 #else
136 static inline void begin_spin(volatile unsigned char *lock)
137 {
138         asm("1: ldstub [%i0], %l0");
139         asm("cmp %l0,0");
140         asm("bne 1b");
141         asm("nop");
142 }
143 #define BEGIN_SPIN(tmp,lock) begin_spin(&lock);
144 #define END_SPIN(lock) ((lock) = 0);
145 #endif
146
147 extern volatile unsigned char _wapi_sparc_lock;
148
149 static inline gint32 InterlockedCompareExchange(volatile gint32 *dest, gint32 exch, gint32 comp)
150 {
151         int tmp;
152         gint32 old;
153
154         BEGIN_SPIN(tmp,_wapi_sparc_lock)
155
156         old = *dest;
157         if (old==comp) {
158                 *dest=exch;
159         }
160
161         END_SPIN(_wapi_sparc_lock)
162
163         return(old);
164 }
165
166 static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp)
167 {
168         int tmp;
169         gpointer old;
170
171         BEGIN_SPIN(tmp,_wapi_sparc_lock)
172
173         old = *dest;
174         if (old==comp) {
175                 *dest=exch;
176         }
177
178         END_SPIN(_wapi_sparc_lock)
179
180         return(old);
181 }
182
183 static inline gint32 InterlockedIncrement(volatile gint32 *dest)
184 {
185         int tmp;
186         gint32 ret;
187
188         BEGIN_SPIN(tmp,_wapi_sparc_lock)
189
190         (*dest)++;
191         ret = *dest;
192
193         END_SPIN(_wapi_sparc_lock)
194
195         return(ret);
196 }
197
198 static inline gint32 InterlockedDecrement(volatile gint32 *dest)
199 {
200         int tmp;
201         gint32 ret;
202
203         BEGIN_SPIN(tmp,_wapi_sparc_lock)
204
205         (*dest)--;
206         ret = *dest;
207
208         END_SPIN(_wapi_sparc_lock)
209
210         return(ret);
211 }
212
213 static inline gint32 InterlockedExchange(volatile gint32 *dest, gint32 exch)
214 {
215         int tmp;
216         gint32 ret;
217
218         BEGIN_SPIN(tmp,_wapi_sparc_lock)
219
220         ret = *dest;
221         *dest = exch;
222
223         END_SPIN(_wapi_sparc_lock)
224
225         return(ret);
226 }
227
228 static inline gpointer InterlockedExchangePointer(volatile gpointer *dest, gpointer exch)
229 {
230         int tmp;
231         gpointer ret;
232
233         BEGIN_SPIN(tmp,_wapi_sparc_lock)
234
235         ret = *dest;
236         *dest = exch;
237
238         END_SPIN(_wapi_sparc_lock)
239
240         return(ret);
241 }
242
243 static inline gint32 InterlockedExchangeAdd(volatile gint32 *dest, gint32 add)
244 {
245         int tmp;
246         gint32 ret;
247
248         BEGIN_SPIN(tmp,_wapi_sparc_lock)
249
250         ret = *dest;
251         *dest += add;
252
253         END_SPIN(_wapi_sparc_lock)
254
255         return(ret);
256 }
257
258 #elif __s390__
259
260 #define WAPI_ATOMIC_ASM
261
262 static inline gint32 
263 InterlockedCompareExchange(volatile gint32 *dest,
264                            gint32 exch, gint32 comp)
265 {
266         gint32 old;
267
268         __asm__ __volatile__ ("\tL\t%1,%0\n"
269                               "\tLA\t1,%0\n"
270                               "\tCS\t%3,%2,0(1)\n"
271                               : "=m" (*dest), "=r" (old)
272                               : "r" (exch), "r" (comp)
273                               : "1", "cc");     
274         return(old);
275 }
276
277 #define InterlockedCompareExchangePointer InterlockedCompareExchange
278
279 static inline gint32 
280 InterlockedIncrement(volatile gint32 *val)
281 {
282         gint32 tmp;
283         
284         __asm__ __volatile__ ("\tLA\t2,%1\n"
285                               "\tL\t%0,%1\n"
286                               "\tLR\t1,%0\n"
287                               "\tAHI\t1,1\n"
288                               "0:\tCS\t%0,1,0(2)\n"
289                               "\tJNZ\t0b"
290                               : "=r" (tmp), "+m" (*val)
291                               : : "1", "2", "cc");
292
293         return(tmp+1);
294 }
295
296 static inline gint32 
297 InterlockedDecrement(volatile gint32 *val)
298 {
299         gint32 tmp;
300         
301         __asm__ __volatile__ ("\tLA\t2,%1\n"
302                               "\tL\t%0,%1\n"
303                               "\tLR\t1,%0\n"
304                               "\tAHI\t1,-1\n"
305                               "0:\tCS\t%0,1,0(2)\n"
306                               "\tJNZ\t0b"
307                               : "=r" (tmp), "+m" (*val)
308                               : : "1", "2", "cc");
309
310         return(tmp-1);
311 }
312
313
314 static inline gint32 
315 InterlockedExchange(volatile gint32 *val, gint32 new_val)
316 {
317         gint32 ret;
318         
319         __asm__ __volatile__ ("\tLA\t1,%1\n"
320                               "0:\tL\t%1,%0\n"
321                               "\tCS\t%1,%2,0(1)\n"
322                               "\tJNZ\t0b"
323                               : "+m" (*val), "=r" (ret)
324                               : "r" (new_val)
325                               : "1", "cc");
326
327         return(ret);
328 }
329
330 #define InterlockedExchangePointer InterlockedExchange
331
332 static inline gint32 
333 InterlockedExchangeAdd(volatile gint32 *val, gint32 add)
334 {
335         gint32 ret;
336
337         __asm__ __volatile__ ("\tL\t%0,%1\n"
338                               "\tLR\t1,%0\n"
339                               "\tAR\t1,%2\n"
340                               "\tLA\t2,%1\n"
341                               "0:\tCS\t%0,1,0(2)\n"
342                               "\tJNZ\t0b"
343                               : "=r" (ret), "+m" (*val)
344                               : "r" (add) 
345                               : "1", "2", "cc");
346         
347         return(ret);
348 }
349
350 #elif defined(__ppc__) || defined (__powerpc__)
351 #define WAPI_ATOMIC_ASM
352
353 static inline gint32 InterlockedIncrement(volatile gint32 *val)
354 {
355         gint32 tmp;
356
357         __asm__ __volatile__ ("\n1:\n\t"
358                               "lwarx  %0, 0, %2\n\t"
359                               "addi   %1, %0, 1\n\t"
360                               "stwcx. %1, 0, %2\n\t"
361                               "bne-   1b"
362                               : "=&b" (tmp): "r" (tmp), "r" (val): "cc", "memory");
363         return tmp;
364 }
365
366 static inline gint32 InterlockedDecrement(volatile gint32 *val)
367 {
368         gint32 tmp;
369
370         __asm__ __volatile__ ("\n1:\n\t"
371                               "lwarx  %0, 0, %2\n\t"
372                               "addi   %1, %0, -1\n\t"
373                               "stwcx. %1, 0, %2\n\t"
374                               "bne-   1b"
375                               : "=&b" (tmp) : "r" (tmp), "r" (val): "cc", "memory");
376         return(tmp);
377 }
378
379 #define InterlockedCompareExchangePointer InterlockedCompareExchange
380
381 static inline gint32 InterlockedCompareExchange(volatile gint32 *dest,
382                                                 gint32 exch, gint32 comp) {
383         gint32 tmp = 0;
384
385         __asm__ __volatile__ ("\n1:\n\t"
386                              "lwarx   %0, 0, %1\n\t"
387                              "cmpw    %2, %3\n\t" 
388                              "bne-    2f\n\t"
389                              "stwcx.  %4, 0, %1\n\t"
390                              "bne-    1b\n"
391                              "2:"
392                              : "=r" (tmp)
393                              : "r" (dest), "0" (tmp) ,"r" (comp), "r" (exch): "cc", "memory");
394         return(tmp);
395 }
396
397 static inline gint32 InterlockedExchange(volatile gint32 *dest, gint32 exch)
398 {
399         gint32 tmp;
400
401         __asm__ __volatile__ ("\n1:\n\t"
402                               "lwarx  %0, 0, %1\n\t"
403                               "stwcx. %2, 0, %1\n\t"
404                               "bne    1b"
405                               : "=r" (tmp) : "r" (dest), "r" (exch): "cc", "memory");
406         return(tmp);
407 }
408 #define InterlockedExchangePointer InterlockedExchange
409
410 static inline gint32 InterlockedExchangeAdd(volatile gint32 *dest, gint32 add)
411 {
412         gint32 tmp;
413
414         __asm__ __volatile__ ("\n1:\n\t"
415                               "lwarx  %0, 0, %2\n\t"
416                               "add    %1, %3, %4\n\t"
417                               "stwcx. %1, 0, %2\n\t"
418                               "bne    1b"
419                               : "=r" (tmp), "=r" (add)
420                               : "r" (dest), "0" (tmp), "1" (add) : "cc", "memory");
421         return(tmp);
422 }
423
424 #elif defined(__arm__)
425 #define WAPI_ATOMIC_ASM
426
427 static inline gint32 InterlockedCompareExchange(volatile gint32 *dest, gint32 exch, gint32 comp)
428 {
429         int a, b;
430
431         __asm__ __volatile__ (    "0:\n\t"
432                                   "ldr %1, [%2]\n\t"
433                                   "cmp %1, %4\n\t"
434                                   "bne 1f\n\t"
435                                   "swp %0, %3, [%2]\n\t"
436                                   "cmp %0, %1\n\t"
437                                   "swpne %3, %0, [%2]\n\t"
438                                   "bne 0b\n\t"
439                                   "1:"
440                                   : "=&r" (a), "=&r" (b)
441                                   : "r" (dest), "r" (exch), "r" (comp)
442                                   : "cc", "memory");
443
444         return a;
445 }
446
447 static inline gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp)
448 {
449         gpointer a, b;
450
451         __asm__ __volatile__ (    "0:\n\t"
452                                   "ldr %1, [%2]\n\t"
453                                   "cmp %1, %4\n\t"
454                                   "bne 1f\n\t"
455                                   "swpeq %0, %3, [%2]\n\t"
456                                   "cmp %0, %1\n\t"
457                                   "swpne %3, %0, [%2]\n\t"
458                                   "bne 0b\n\t"
459                                   "1:"
460                                   : "=&r" (a), "=&r" (b)
461                                   : "r" (dest), "r" (exch), "r" (comp)
462                                   : "cc", "memory");
463
464         return a;
465 }
466
467 static inline gint32 InterlockedIncrement(volatile gint32 *dest)
468 {
469         int a, b, c;
470
471         __asm__ __volatile__ (  "0:\n\t"
472                                 "ldr %0, [%3]\n\t"
473                                 "add %1, %0, %4\n\t"
474                                 "swp %2, %1, [%3]\n\t"
475                                 "cmp %0, %2\n\t"
476                                 "swpne %1, %2, [%3]\n\t"
477                                 "bne 0b"
478                                 : "=&r" (a), "=&r" (b), "=&r" (c)
479                                 : "r" (dest), "r" (1)
480                                 : "cc", "memory");
481
482         return b;
483 }
484
485 static inline gint32 InterlockedDecrement(volatile gint32 *dest)
486 {
487         int a, b, c;
488
489         __asm__ __volatile__ (  "0:\n\t"
490                                 "ldr %0, [%3]\n\t"
491                                 "add %1, %0, %4\n\t"
492                                 "swp %2, %1, [%3]\n\t"
493                                 "cmp %0, %2\n\t"
494                                 "swpne %1, %2, [%3]\n\t"
495                                 "bne 0b"
496                                 : "=&r" (a), "=&r" (b), "=&r" (c)
497                                 : "r" (dest), "r" (-1)
498                                 : "cc", "memory");
499
500         return b;
501 }
502
503 static inline gint32 InterlockedExchange(volatile gint32 *dest, gint32 exch)
504 {
505         int a;
506
507         __asm__ __volatile__ (  "swp %0, %2, [%1]"
508                                 : "=&r" (a)
509                                 : "r" (dest), "r" (exch));
510
511         return a;
512 }
513
514 static inline gpointer InterlockedExchangePointer(volatile gpointer *dest, gpointer exch)
515 {
516         gpointer a;
517
518         __asm__ __volatile__ (  "swp %0, %2, [%1]"
519                                 : "=&r" (a)
520                                 : "r" (dest), "r" (exch));
521
522         return a;
523 }
524
525 static inline gint32 InterlockedExchangeAdd(volatile gint32 *dest, gint32 add)
526 {
527         int a, b, c;
528
529         __asm__ __volatile__ (  "0:\n\t"
530                                 "ldr %0, [%3]\n\t"
531                                 "add %1, %0, %4\n\t"
532                                 "swp %2, %1, [%3]\n\t"
533                                 "cmp %0, %2\n\t"
534                                 "swpne %1, %2, [%3]\n\t"
535                                 "bne 0b"
536                                 : "=&r" (a), "=&r" (b), "=&r" (c)
537                                 : "r" (dest), "r" (add)
538                                 : "cc", "memory");
539
540         return a;
541 }
542
543 #else
544
545 extern gint32 InterlockedCompareExchange(volatile gint32 *dest, gint32 exch, gint32 comp);
546 extern gpointer InterlockedCompareExchangePointer(volatile gpointer *dest, gpointer exch, gpointer comp);
547 extern gint32 InterlockedIncrement(volatile gint32 *dest);
548 extern gint32 InterlockedDecrement(volatile gint32 *dest);
549 extern gint32 InterlockedExchange(volatile gint32 *dest, gint32 exch);
550 extern gpointer InterlockedExchangePointer(volatile gpointer *dest, gpointer exch);
551 extern gint32 InterlockedExchangeAdd(volatile gint32 *dest, gint32 add);
552
553 #if defined(__hpux) && !defined(__GNUC__)
554 #define WAPI_ATOMIC_ASM
555 #endif
556
557 #endif
558
559 #endif /* _WAPI_ATOMIC_H_ */