Add atomic_inc()/atomic_dec(), and use them to replace gc_running_mutex
[ghc-hetmet.git] / includes / SMP.h
1 /* ----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team, 2005-2008
4  *
5  * Macros for multi-CPU support
6  *
7  * -------------------------------------------------------------------------- */
8
9 #ifndef SMP_H
10 #define SMP_H
11
12 /* THREADED_RTS is currently not compatible with the following options:
13  *
14  *      PROFILING (but only 1 CPU supported)
15  *      TICKY_TICKY
16  *      Unregisterised builds are ok, but only 1 CPU supported.
17  */
18
19 #if defined(THREADED_RTS)
20
21 #if defined(TICKY_TICKY)
22 #error Build options incompatible with THREADED_RTS.
23 #endif
24
25 /* ----------------------------------------------------------------------------
26    Atomic operations
27    ------------------------------------------------------------------------- */
28    
29 #if !IN_STG_CODE
30 // We only want write_barrier() declared in .hc files.  Defining the
31 // other inline functions here causes type mismatch errors from gcc,
32 // because the generated C code is assuming that there are no
33 // prototypes in scope.
34
35 /* 
36  * The atomic exchange operation: xchg(p,w) exchanges the value
37  * pointed to by p with the value w, returning the old value.
38  *
39  * Used for locking closures during updates (see lockClosure() below)
40  * and the MVar primops.
41  */
42 EXTERN_INLINE StgWord xchg(StgPtr p, StgWord w);
43
44 /* 
45  * Compare-and-swap.  Atomically does this:
46  *
47  * cas(p,o,n) { 
48  *    r = *p; 
49  *    if (r == o) { *p = n }; 
50  *    return r;
51  * }
52  */
53 EXTERN_INLINE StgWord cas(StgVolatilePtr p, StgWord o, StgWord n);
54
55 /*
56  * Atomic increment
57  *
58  * atomic_inc(p) {
59  *   return ++(*p);
60  * }
61  */
62 EXTERN_INLINE StgWord atomic_inc(StgVolatilePtr p);
63
64 /*
65  * Atomic decrement
66  *
67  * atomic_dec(p) {
68  *   return --(*p);
69  * }
70  */
71 EXTERN_INLINE StgWord atomic_dec(StgVolatilePtr p);
72
73 #endif // !IN_STG_CODE
74
75 /*
76  * Various kinds of memory barrier.
77  *  write_barrier: prevents future stores occurring before prededing stores.
78  *  store_load_barrier: prevents future loads occurring before preceding stores.
79  *  load_load_barrier: prevents future loads occurring before earlier stores.
80  *
81  * Reference for these: "The JSR-133 Cookbook for Compiler Writers"
82  * http://gee.cs.oswego.edu/dl/jmm/cookbook.html
83  *
84  * To check whether you got these right, try the test in 
85  *   testsuite/tests/ghc-regress/rts/testwsdeque.c
86  * This tests the work-stealing deque implementation, which relies on
87  * properly working store_load and load_load memory barriers.
88  */ 
89 EXTERN_INLINE void write_barrier(void);
90 EXTERN_INLINE void store_load_barrier(void);
91 EXTERN_INLINE void load_load_barrier(void);
92
93 /* ----------------------------------------------------------------------------
94    Implementations
95    ------------------------------------------------------------------------- */
96
97 #if !IN_STG_CODE
98
99 /* 
100  * NB: the xchg instruction is implicitly locked, so we do not need
101  * a lock prefix here. 
102  */
103 EXTERN_INLINE StgWord
104 xchg(StgPtr p, StgWord w)
105 {
106     StgWord result;
107 #if i386_HOST_ARCH || x86_64_HOST_ARCH
108     result = w;
109     __asm__ __volatile__ (
110           "xchg %1,%0"
111           :"+r" (result), "+m" (*p)
112           : /* no input-only operands */
113         );
114 #elif powerpc_HOST_ARCH
115     __asm__ __volatile__ (
116         "1:     lwarx     %0, 0, %2\n"
117         "       stwcx.    %1, 0, %2\n"
118         "       bne-      1b"
119         :"=&r" (result)
120         :"r" (w), "r" (p)
121     );
122 #elif sparc_HOST_ARCH
123     result = w;
124     __asm__ __volatile__ (
125         "swap %1,%0"
126         : "+r" (result), "+m" (*p)
127         : /* no input-only operands */
128       );
129 #elif !defined(WITHSMP)
130     result = *p;
131     *p = w;
132 #else
133 #error xchg() unimplemented on this architecture
134 #endif
135     return result;
136 }
137
138 /* 
139  * CMPXCHG - the single-word atomic compare-and-exchange instruction.  Used 
140  * in the STM implementation.
141  */
142 EXTERN_INLINE StgWord
143 cas(StgVolatilePtr p, StgWord o, StgWord n)
144 {
145 #if i386_HOST_ARCH || x86_64_HOST_ARCH
146     __asm__ __volatile__ (
147           "lock\ncmpxchg %3,%1"
148           :"=a"(o), "=m" (*(volatile unsigned int *)p) 
149           :"0" (o), "r" (n));
150     return o;
151 #elif powerpc_HOST_ARCH
152     StgWord result;
153     __asm__ __volatile__ (
154         "1:     lwarx     %0, 0, %3\n"
155         "       cmpw      %0, %1\n"
156         "       bne       2f\n"
157         "       stwcx.    %2, 0, %3\n"
158         "       bne-      1b\n"
159         "2:"
160         :"=&r" (result)
161         :"r" (o), "r" (n), "r" (p)
162         :"cc", "memory"
163     );
164     return result;
165 #elif sparc_HOST_ARCH
166     __asm__ __volatile__ (
167         "cas [%1], %2, %0"
168         : "+r" (n)
169         : "r" (p), "r" (o)
170         : "memory"
171     );
172     return n;
173 #elif !defined(WITHSMP)
174     StgWord result;
175     result = *p;
176     if (result == o) {
177         *p = n;
178     }
179     return result;
180 #else
181 #error cas() unimplemented on this architecture
182 #endif
183 }
184
185 EXTERN_INLINE StgWord
186 atomic_inc(StgVolatilePtr p)
187 {
188 #if 0 // defined(i386_HOST_ARCH) || defined(x86_64_HOST_ARCH)
189     StgWord r;
190     r = 1;
191     __asm__ __volatile__ (
192         "lock\nxadd %0,%1":
193             "+r" (r), "+m" (*p):
194     );
195     return r;
196 #else
197     StgWord old, new;
198     do {
199         old = *p;
200         new = old + 1;
201     } while (cas(p, old, new) != old);
202     return new;
203 #endif
204 }
205
206 EXTERN_INLINE StgWord
207 atomic_dec(StgVolatilePtr p)
208 {
209 #if 0 //defined(i386_HOST_ARCH) || defined(x86_64_HOST_ARCH)
210     StgWord r;
211     r = (StgWord)-1;
212     __asm__ __volatile__ (
213         "lock\nxadd %0,%1":
214             "+r" (r), "+m" (*p):
215     );
216     return r;
217 #else
218     StgWord old, new;
219     do {
220         old = *p;
221         new = old - 1;
222     } while (cas(p, old, new) != old);
223     return new;
224 #endif
225 }
226
227 #endif // !IN_STG_CODE
228
229 /*
230  * We need to tell both the compiler AND the CPU about the barriers.
231  * It's no good preventing the CPU from reordering the operations if
232  * the compiler has already done so - hence the "memory" restriction
233  * on each of the barriers below.
234  */
235 EXTERN_INLINE void
236 write_barrier(void) {
237 #if i386_HOST_ARCH || x86_64_HOST_ARCH
238     __asm__ __volatile__ ("" : : : "memory");
239 #elif powerpc_HOST_ARCH
240     __asm__ __volatile__ ("lwsync" : : : "memory");
241 #elif sparc_HOST_ARCH
242     /* Sparc in TSO mode does not require store/store barriers. */
243     __asm__ __volatile__ ("" : : : "memory");
244 #elif !defined(WITHSMP)
245     return;
246 #else
247 #error memory barriers unimplemented on this architecture
248 #endif
249 }
250
251 EXTERN_INLINE void
252 store_load_barrier(void) {
253 #if i386_HOST_ARCH
254     __asm__ __volatile__ ("lock; addl $0,0(%%esp)" : : : "memory");
255 #elif x86_64_HOST_ARCH
256     __asm__ __volatile__ ("lock; addq $0,0(%%rsp)" : : : "memory");
257 #elif powerpc_HOST_ARCH
258     __asm__ __volatile__ ("sync" : : : "memory");
259 #elif sparc_HOST_ARCH
260     __asm__ __volatile__ ("membar #StoreLoad" : : : "memory");
261 #elif !defined(WITHSMP)
262     return;
263 #else
264 #error memory barriers unimplemented on this architecture
265 #endif
266 }
267
268 EXTERN_INLINE void
269 load_load_barrier(void) {
270 #if i386_HOST_ARCH
271     __asm__ __volatile__ ("" : : : "memory");
272 #elif x86_64_HOST_ARCH
273     __asm__ __volatile__ ("" : : : "memory");
274 #elif powerpc_HOST_ARCH
275     __asm__ __volatile__ ("lwsync" : : : "memory");
276 #elif sparc_HOST_ARCH
277     /* Sparc in TSO mode does not require load/load barriers. */
278     __asm__ __volatile__ ("" : : : "memory");
279 #elif !defined(WITHSMP)
280     return;
281 #else
282 #error memory barriers unimplemented on this architecture
283 #endif
284 }
285
286 /* ---------------------------------------------------------------------- */
287 #else /* !THREADED_RTS */
288
289 #define write_barrier()      /* nothing */
290 #define store_load_barrier() /* nothing */
291 #define load_load_barrier()  /* nothing */
292
293 INLINE_HEADER StgWord
294 xchg(StgPtr p, StgWord w)
295 {
296     StgWord old = *p;
297     *p = w;
298     return old;
299 }
300
301 STATIC_INLINE StgWord
302 cas(StgVolatilePtr p, StgWord o, StgWord n)
303 {
304     StgWord result;
305     result = *p;
306     if (result == o) {
307         *p = n;
308     }
309     return result;
310 }
311
312 INLINE_HEADER StgWord
313 atomic_inc(StgVolatilePtr p)
314 {
315     return ++(*p);
316 }
317
318 INLINE_HEADER StgWord
319 atomic_dec(StgVolatilePtr p)
320 {
321     return --(*p);
322 }
323
324 #endif /* !THREADED_RTS */
325
326 #endif /* SMP_H */