034412e6b1189d1521983c1abcd4b7b60dfbb3c7
[ghc-hetmet.git] / ghc / includes / Updates.h
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team, 1998-2004
4  *
5  * Performing updates.
6  *
7  * ---------------------------------------------------------------------------*/
8
9 #ifndef UPDATES_H
10 #define UPDATES_H
11
12 /* -----------------------------------------------------------------------------
13    Updates
14
15    We have two layers of update macros.  The top layer, UPD_IND() and
16    friends perform all the work of an update.  In detail:
17
18       - if the closure being updated is a blocking queue, then all the
19         threads waiting on the blocking queue are updated.
20
21       - then the lower level updateWithIndirection() macro is invoked 
22         to actually replace the closure with an indirection (see below).
23
24    -------------------------------------------------------------------------- */
25
26 #ifdef TICKY_TICKY
27 # define UPD_IND(updclosure, heapptr) \
28    UPD_PERM_IND(updclosure,heapptr)
29 # define UPD_SPEC_IND(updclosure, ind_info, heapptr, and_then) \
30    UPD_PERM_IND(updclosure,heapptr); and_then
31 # define NOBH_UPD_SPEC_IND(updclosure, ind_info, heapptr, and_then) \
32    NOBH_UPD_PERM_IND(updclosure,heapptr); and_then
33 #else
34 #  define SEMI ;
35 # define UPD_IND(updclosure, heapptr) \
36    UPD_REAL_IND(updclosure,INFO_PTR(stg_IND_info),heapptr,SEMI)
37 # define UPD_SPEC_IND(updclosure, ind_info, heapptr, and_then) \
38    UPD_REAL_IND(updclosure,ind_info,heapptr,and_then)
39 # define NOBH_UPD_SPEC_IND(updclosure, ind_info, heapptr, and_then) \
40    NOBH_UPD_REAL_IND(updclosure,ind_info,heapptr,and_then)
41 #endif
42
43 /* These macros have to work in both C and C--, so here's the
44  * impedence matching:
45  */
46 #ifdef CMINUSMINUS
47 #define BLOCK_BEGIN
48 #define BLOCK_END
49 #define DECLARE_IPTR(info)  W_ info
50 #define FCALL               foreign "C"
51 #define INFO_PTR(info)      info
52 #define ARG_PTR             "ptr"
53 #else
54 #define BLOCK_BEGIN         {
55 #define BLOCK_END           }
56 #define DECLARE_IPTR(info)  const StgInfoTable *(info)
57 #define FCALL               /* nothing */
58 #define INFO_PTR(info)      &info
59 #define StgBlockingQueue_blocking_queue(closure) \
60     (((StgBlockingQueue *)closure)->blocking_queue)
61 #define ARG_PTR             /* nothing */
62 #endif
63
64 /* UPD_IND actually does a PERM_IND if TICKY_TICKY is on;
65    if you *really* need an IND use UPD_REAL_IND
66  */
67 #define UPD_REAL_IND(updclosure, ind_info, heapptr, and_then)   \
68         BLOCK_BEGIN                                             \
69         DECLARE_IPTR(info);                                     \
70         info = GET_INFO(updclosure);                            \
71         AWAKEN_BQ(info,updclosure);                             \
72         updateWithIndirection(GET_INFO(updclosure), ind_info,   \
73                               updclosure,                       \
74                               heapptr,                          \
75                               and_then);                        \
76         BLOCK_END
77
78 #define NOBH_UPD_REAL_IND(updclosure, ind_info, heapptr, and_then)      \
79         BLOCK_BEGIN                                             \
80         updateWithIndirection(GET_INFO(updclosure), ind_info,   \
81                               updclosure,                       \
82                               heapptr,                          \
83                               and_then);                        \
84         BLOCK_END
85
86 #if defined(PROFILING) || defined(TICKY_TICKY)
87 #define UPD_PERM_IND(updclosure, heapptr)       \
88         BLOCK_BEGIN                             \
89         DECLARE_IPTR(info);                     \
90         info = GET_INFO(updclosure);            \
91         AWAKEN_BQ(info,updclosure);             \
92         updateWithPermIndirection(info,         \
93                                   updclosure,   \
94                                   heapptr);     \
95         BLOCK_END
96
97 #define NOBH_UPD_PERM_IND(updclosure, heapptr)  \
98         BLOCK_BEGIN                             \
99         updateWithPermIndirection(GET_INFO(updclosure),         \
100                                   updclosure,   \
101                                   heapptr);     \
102         BLOCK_END
103 #endif
104
105 #if defined(RTS_SUPPORTS_THREADS)
106
107 # ifdef TICKY_TICKY
108 #  define UPD_IND_NOLOCK(updclosure, heapptr)   \
109         BLOCK_BEGIN                             \
110         DECLARE_IPTR(info);                     \
111         info = GET_INFO(updclosure);            \
112         AWAKEN_BQ_NOLOCK(info,updclosure);      \
113         updateWithPermIndirection(info,         \
114                                   updclosure,   \
115                                   heapptr);     \
116         BLOCK_END
117 # else
118 #  define UPD_IND_NOLOCK(updclosure, heapptr)                   \
119         BLOCK_BEGIN                                             \
120         DECLARE_IPTR(info);                                     \
121         info = GET_INFO(updclosure);                            \
122         AWAKEN_BQ_NOLOCK(info,updclosure);                      \
123         updateWithIndirection(info, INFO_PTR(stg_IND_info),     \
124                               updclosure,                       \
125                               heapptr,);                        \
126         BLOCK_END
127 # endif
128
129 #else
130 #define UPD_IND_NOLOCK(updclosure,heapptr) UPD_IND(updclosure,heapptr)
131 #endif
132
133 /* -----------------------------------------------------------------------------
134    Awaken any threads waiting on a blocking queue (BLACKHOLE_BQ).
135    -------------------------------------------------------------------------- */
136
137 #if defined(PAR) 
138
139 /* 
140    In a parallel setup several types of closures might have a blocking queue:
141      BLACKHOLE_BQ ... same as in the default concurrent setup; it will be
142                       reawakened via calling UPD_IND on that closure after
143                       having finished the computation of the graph
144      FETCH_ME_BQ  ... a global indirection (FETCH_ME) may be entered by a 
145                       local TSO, turning it into a FETCH_ME_BQ; it will be
146                       reawakened via calling processResume
147      RBH          ... a revertible black hole may be entered by another 
148                       local TSO, putting it onto its blocking queue; since
149                       RBHs only exist while the corresponding closure is in 
150                       transit, they will be reawakened via calling 
151                       convertToFetchMe (upon processing an ACK message)
152
153    In a parallel setup a blocking queue may contain 3 types of closures:
154      TSO           ... as in the default concurrent setup
155      BLOCKED_FETCH ... indicating that a TSO on another PE is waiting for
156                        the result of the current computation
157      CONSTR        ... an RBHSave closure (which contains data ripped out of
158                        the closure to make room for a blocking queue; since
159                        it only contains data we use the exisiting type of
160                        a CONSTR closure); this closure is the end of a 
161                        blocking queue for an RBH closure; it only exists in
162                        this kind of blocking queue and must be at the end
163                        of the queue
164 */                    
165 extern void awakenBlockedQueue(StgBlockingQueueElement *q, StgClosure *node);
166 #define DO_AWAKEN_BQ(bqe, node)  STGCALL2(awakenBlockedQueue, bqe, node);
167
168 #define AWAKEN_BQ(info,closure)                                         \
169         if (info == &stg_BLACKHOLE_BQ_info ||               \
170             info == &stg_FETCH_ME_BQ_info ||                \
171             get_itbl(closure)->type == RBH) {                           \
172                 DO_AWAKEN_BQ(((StgBlockingQueue *)closure)->blocking_queue, closure);                           \
173         }
174
175 #elif defined(GRAN)
176
177 extern void awakenBlockedQueue(StgBlockingQueueElement *q, StgClosure *node);
178 #define DO_AWAKEN_BQ(bq, node)  STGCALL2(awakenBlockedQueue, bq, node);
179
180 /* In GranSim we don't have FETCH_ME or FETCH_ME_BQ closures, so they are
181    not checked. The rest of the code is the same as for GUM.
182 */
183 #define AWAKEN_BQ(info,closure)                                         \
184         if (info == &stg_BLACKHOLE_BQ_info ||               \
185             get_itbl(closure)->type == RBH) {                           \
186                 DO_AWAKEN_BQ(((StgBlockingQueue *)closure)->blocking_queue, closure);                           \
187         }
188
189
190 #else /* !GRAN && !PAR */
191
192 #define DO_AWAKEN_BQ(closure)   \
193         FCALL awakenBlockedQueue(StgBlockingQueue_blocking_queue(closure) ARG_PTR);
194
195 #define AWAKEN_BQ(info,closure)                                         \
196         if (info == INFO_PTR(stg_BLACKHOLE_BQ_info)) {                  \
197           DO_AWAKEN_BQ(closure);                                        \
198         }
199
200 #define AWAKEN_STATIC_BQ(info,closure)                                  \
201         if (info == INFO_PTR(stg_BLACKHOLE_BQ_STATIC_info)) {           \
202           DO_AWAKEN_BQ(closure);                                        \
203         }
204
205 #ifdef RTS_SUPPORTS_THREADS
206 #define DO_AWAKEN_BQ_NOLOCK(closure) \
207         FCALL awakenBlockedQueueNoLock(StgBlockingQueue_blocking_queue(closure) ARG_PTR);
208
209 #define AWAKEN_BQ_NOLOCK(info,closure)                                  \
210         if (info == INFO_PTR(stg_BLACKHOLE_BQ_info)) {                  \
211           DO_AWAKEN_BQ_NOLOCK(closure);                                 \
212         }
213 #endif
214 #endif /* GRAN || PAR */
215
216 /* -----------------------------------------------------------------------------
217    Updates: lower-level macros which update a closure with an
218    indirection to another closure.
219
220    There are several variants of this code.
221
222        PROFILING:
223    -------------------------------------------------------------------------- */
224
225 /* LDV profiling:
226  * We call LDV_recordDead_FILL_SLOP_DYNAMIC(p1) regardless of the generation in 
227  * which p1 resides.
228  *
229  * Note: 
230  *   After all, we do *NOT* need to call LDV_RECORD_CREATE() for both IND and 
231  *   IND_OLDGEN closures because they are inherently used. But, it corrupts
232  *   the invariants that every closure keeps its creation time in the profiling
233  *  field. So, we call LDV_RECORD_CREATE().
234  */
235
236 /* In the DEBUG case, we also zero out the slop of the old closure,
237  * so that the sanity checker can tell where the next closure is.
238  *
239  * Two important invariants: we should never try to update a closure
240  * to point to itself, and the closure being updated should not
241  * already have been updated (the mutable list will get messed up
242  * otherwise).
243  */
244 #if !defined(DEBUG)
245
246 #define DEBUG_FILL_SLOP(p) /* nothing */
247
248 #else  /* DEBUG */
249
250 #ifdef CMINUSMINUS
251
252 #define DEBUG_FILL_SLOP(p)                      \
253   W_ inf;                                       \
254   W_ np;                                        \
255   W_ nw;                                        \
256   W_ i;                                         \
257   inf = %GET_STD_INFO(p);                       \
258   np = TO_W_(%INFO_PTRS(inf));                  \
259   nw = TO_W_(%INFO_NPTRS(inf));                 \
260   if (%INFO_TYPE(inf) != HALF_W_(THUNK_SELECTOR)) {     \
261     i = 0;                                      \
262     for:                                        \
263       if (i < np + nw) {                        \
264         StgClosure_payload(p,i) = 0;            \
265         i = i + 1;                              \
266         goto for;                               \
267       }                                         \
268   }
269
270
271 #else /* !CMINUSMINUS */
272
273 INLINE_HEADER void
274 DEBUG_FILL_SLOP(StgClosure *p)
275 {                                               
276     StgInfoTable *inf = get_itbl(p);            
277     nat np = inf->layout.payload.ptrs,          
278         nw = inf->layout.payload.nptrs, i;
279     if (inf->type != THUNK_SELECTOR) {
280         for (i = 0; i < np + nw; i++) {
281             ((StgClosure *)p)->payload[i] = 0;
282         }
283     }
284 }
285
286 #endif /* CMINUSMINUS */
287 #endif /* DEBUG */
288
289 /* We have two versions of this macro (sadly), one for use in C-- code,
290  * and the other for C.
291  *
292  * The and_then argument is a performance hack so that we can paste in
293  * the continuation code directly.  It helps shave a couple of
294  * instructions off the common case in the update code, which is
295  * worthwhile (the update code is often part of the inner loop).
296  * (except that gcc now appears to common up this code again and
297  * invert the optimisation.  Grrrr --SDM).
298  */
299 #ifdef CMINUSMINUS
300 #define generation(n) (W_[generations] + n*SIZEOF_generation)
301 #define updateWithIndirection(info, ind_info, p1, p2, and_then) \
302     W_ bd;                                                      \
303                                                                 \
304 /*    ASSERT( p1 != p2 && !closure_IND(p1) );                   \
305  */ LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1);                      \
306     bd = Bdescr(p1);                                            \
307     if (bdescr_gen_no(bd) == 0 :: CInt) {                       \
308       StgInd_indirectee(p1) = p2;                               \
309       SET_INFO(p1, ind_info);                                   \
310       LDV_RECORD_CREATE(p1);                                    \
311       TICK_UPD_NEW_IND();                                       \
312       and_then;                                                 \
313     } else {                                                    \
314       if (info != stg_BLACKHOLE_BQ_info) {                      \
315         DEBUG_FILL_SLOP(p1);                                    \
316         foreign "C" recordMutableGen(p1 "ptr",                  \
317                  generation(TO_W_(bdescr_gen_no(bd))) "ptr");   \
318       }                                                         \
319       StgInd_indirectee(p1) = p2;                               \
320       SET_INFO(p1, stg_IND_OLDGEN_info);                        \
321       LDV_RECORD_CREATE(p1);                                    \
322       TICK_UPD_OLD_IND();                                       \
323       and_then;                                                 \
324   }
325 #else
326 #define updateWithIndirection(_info, ind_info, p1, p2, and_then)        \
327   {                                                                     \
328     bdescr *bd;                                                         \
329                                                                         \
330     ASSERT( (P_)p1 != (P_)p2 && !closure_IND(p1) );                     \
331     LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1);                              \
332     bd = Bdescr((P_)p1);                                                \
333     if (bd->gen_no == 0) {                                              \
334       ((StgInd *)p1)->indirectee = p2;                                  \
335       SET_INFO(p1, ind_info);                                           \
336       LDV_RECORD_CREATE(p1);                                            \
337       TICK_UPD_NEW_IND();                                               \
338       and_then;                                                         \
339     } else {                                                            \
340       if (_info != &stg_BLACKHOLE_BQ_info) {                            \
341         DEBUG_FILL_SLOP(p1);                                            \
342         recordMutableGen(p1, &generations[bd->gen_no]);                 \
343       }                                                                 \
344       ((StgInd *)p1)->indirectee = p2;                                  \
345       SET_INFO(p1, &stg_IND_OLDGEN_info);                               \
346       TICK_UPD_OLD_IND();                                               \
347       and_then;                                                         \
348     }                                                                   \
349   }
350 #endif
351
352 /* The permanent indirection version isn't performance critical.  We
353  * therefore use an inline C function instead of the C-- macro.
354  */
355 #ifndef CMINUSMINUS
356 INLINE_HEADER void
357 updateWithPermIndirection(const StgInfoTable *info, 
358                           StgClosure *p1,
359                           StgClosure *p2) 
360 {
361   bdescr *bd;
362
363   ASSERT( p1 != p2 && !closure_IND(p1) );
364
365   // @LDV profiling
366   // Destroy the old closure.
367   // Nb: LDV_* stuff cannot mix with ticky-ticky
368   LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p1);
369
370   bd = Bdescr((P_)p1);
371   if (bd->gen_no == 0) {
372     ((StgInd *)p1)->indirectee = p2;
373     SET_INFO(p1, &stg_IND_PERM_info);
374     // @LDV profiling
375     // We have just created a new closure.
376     LDV_RECORD_CREATE(p1);
377     TICK_UPD_NEW_PERM_IND(p1);
378   } else {
379     if (info != &stg_BLACKHOLE_BQ_info) {
380         recordMutableGen(p1, &generations[bd->gen_no]);
381     }
382     ((StgInd *)p1)->indirectee = p2;
383     SET_INFO(p1, &stg_IND_OLDGEN_PERM_info);
384     // @LDV profiling
385     // We have just created a new closure.
386     LDV_RECORD_CREATE(p1);
387     TICK_UPD_OLD_PERM_IND();
388   }
389 }
390 #endif
391
392 #endif /* UPDATES_H */