1 \section[SMupdate.h]{Update interface}
3 This interface provides a second level of abstraction from the storage
4 manager hiding all the nasties associated with updates, indirections,
12 %************************************************************************
14 \subsubsection[update-frames]{Pushing Update Frames}
16 %************************************************************************
18 If a closure is to be updated with the result of the computation an
19 update frame must be pushed onto the B stack.
21 A {\em Standard update frame} contains (in order from the top of the
24 \item The return vector.
25 \item The closure to be updated.
26 \item Saved @SuB@ (points to the next update frame).
30 Note: We used to keep the {\em offsets} smashed into one word, but the
31 introduction of strict evaluation meant we could overflow this.
33 [Don't really believe this cost-centre stuff WDP 94/07]
35 If we are keeping track of the current cost centre we have to make the
39 The current cost centre, @CCC@, is added as an additional field to the
40 update frames described above. It is the last field in the frame.
41 When the update is executed the cost centre is restored.
44 A special restore cost centre frame is introduced which does not
45 contain a closure to update, but just a cost centre to restore.
48 The different update frame sizes, @STD_UF_SIZE@, @CON_UF_SIZE@ and
49 optionally @RCC_UF_SIZE@, and the offsets within a frame (@UF_RET@,
50 @UF_UPDATEE@, etc) are declared in \tr{GhcConstants.lh}.
52 We now have the macros to push update frames. They are passed the
53 update @target@ and the A and B stack offsets at which the current top
54 of stack is found. E.g. @SpX + SpX_offset@ points to the top word on
55 the stack. The update frame is pushed directly above this position on
56 the B stack. @SpB + SpB_offset + xxx_UF_SIZE@ gives the topmost word
57 of the update frame, from which we subtract the offsets.
59 ``A picture is worth five thousand bytes.''
61 A stk | |-----------------------|
63 |-----------------------|
65 |-----------------------|
67 |-----------------------|
72 |=======================| (new update frame)
73 | upd code or vector | <- new SuB
74 |-----------------------|
76 |-----------------------|
77 | SuB (grip: SuB_off) | (SuX_off = abs ( new SuX - prev SuX );e.g., 7 for SuB
78 |-----------------------|
79 | SuA (grip: SuA_off) |
80 |-----------------------|
82 |=======================|
83 | | <- SpB now [really (SpB + SpB_offset)]
84 |-----------------------|
86 |-----------------------|
88 |=======================| (prev update frame [e.g.])
89 | upd code or vector | <- prev SuB
90 |-----------------------|
92 |-----------------------|
94 |-----------------------|
96 |-----------------------|
98 |=======================|
100 ^ |-----------------------|
105 I_ SqueezeUpdateFrames PROTO((P_, P_, P_));
107 EXTDATA_RO(vtbl_StdUpdFrame);
108 EXTFUN(StdUpdFrameDirectReturn);
110 EXTFUN(StdErrorCode); /* Where should this go? */
120 EXTFUN(IndUpdRetDir);
123 Note that UNVEC() is used to select whole statements (declarations) as
124 well as labels. Please don't put parentheses around the expansion.
127 #ifdef __STG_REV_TBLS__
128 #define RVREL(offset) (-(offset)-1)
129 #define DIRECT(target) (target)
130 #define UNVEC(direct,vector) direct
132 #define RVREL(offset) (offset)
133 #define DIRECT(target) (*(target))
134 #define UNVEC(direct,vector) vector
138 /* for stack chunking */
139 extern const W_ vtbl_Underflow[];
140 EXTFUN(UnderflowDirectReturn);
141 EXTFUN(UnderflowVect0);
142 EXTFUN(UnderflowVect1);
143 EXTFUN(UnderflowVect2);
144 EXTFUN(UnderflowVect3);
145 EXTFUN(UnderflowVect4);
146 EXTFUN(UnderflowVect5);
147 EXTFUN(UnderflowVect6);
148 EXTFUN(UnderflowVect7);
149 EXTFUN(StackUnderflowEnterNode);
150 EXTFUN(CommonUnderflow);
151 EXTFUN(PrimUnderflow);
152 #endif /* CONCURRENT */
154 /* Now, we always use pointers in update frame, even in the threaded world */
156 #define PUSH_RET(frame, rv) (frame)[BREL(UF_RET)] = (W_)(rv)
157 #define PUSH_UPDATEE(frame, updatee) (frame)[BREL(UF_UPDATEE)] = (W_)(updatee)
158 #define PUSH_SuB(frame, sub) (frame)[BREL(UF_SUB)] = (W_)(sub)
159 #define PUSH_SuA(frame, sua) (frame)[BREL(UF_SUA)] = (W_)(sua)
161 #if defined(PROFILING)
162 #define PUSH_STD_CCC(frame) (frame)[BREL(UF_COST_CENTRE)] = (W_)(CCC)
164 #define PUSH_STD_CCC(frame)
167 /* When GRABing, "frame" pts to an update frame */
169 #define GRAB_RET(frame) ((void *)((frame)[BREL(UF_RET)]))
170 #define GRAB_SuB(frame) ((P_)((frame)[BREL(UF_SUB)]))
171 #define GRAB_SuA(frame) ((PP_)((frame)[BREL(UF_SUA)]))
172 #define GRAB_UPDATEE(frame) ((P_)((frame)[BREL(UF_UPDATEE)]))
173 #define GRAB_COST_CENTRE(frame) ((CostCentre)((frame)[BREL(UF_COST_CENTRE)]))
175 #define PUSH_STD_UPD_FRAME(target, SpA_offset, SpB_offset) \
178 UPDF_STD_PUSHED(); /* ticky-ticky, spat */ \
179 __frame = SpB - BREL(SpB_offset + STD_UF_SIZE); \
180 PUSH_RET(__frame, RetReg); \
181 PUSH_SuB(__frame, SuB); \
182 PUSH_SuA(__frame, SuA); \
183 PUSH_UPDATEE(__frame, target); \
184 PUSH_STD_CCC(__frame); \
186 SuA = SpA - AREL(SpA_offset); \
189 #define POP_STD_UPD_FRAME() \
191 RetReg = GRAB_RET(SpB); \
192 SuB = GRAB_SuB(SpB); \
193 SuA = GRAB_SuA(SpB); \
194 SpB += BREL(STD_UF_SIZE); \
200 %************************************************************************
202 \subsubsection[black-hole-overwrite]{Overwriting with Black Holes}
204 %************************************************************************
206 An updatable closure may be overwritten with a black hole so that
207 the free variables in the closure being evaluated are not kept alive.
208 This may be done on entering the closure or later by the garbage
212 EXTDATA_RO(BH_UPD_info);
213 EXTFUN(BH_UPD_entry);
214 EXTDATA_RO(BH_SINGLE_info);
215 EXTFUN(BH_SINGLE_entry);
217 #define UPD_BH(heapptr,infolbl) INFO_PTR(heapptr) = (W_) infolbl
220 The following macros are actually planted by the code generator. They
221 allow us to delay the decision about if/when we black hole. It should
222 be noted that single entry closures do not have update frames which
223 can be traced by the garbage collector. It is only possibly to
224 overwrite with a black hole on entry.
226 In the sequential system, updatable closures are not black-holed until GC.
227 When GC occurs, the only active updatable closures are those with update
228 frames on the stack, so the GC routine can walk the stack frames to find
229 the updatable closures to black hole (primarily for plugging space leaks).
230 This approach saves the overhead of black-holing every updatable closure on
233 In the parallel system, however, it is essential that updatable closures
234 be black-holed immediately on entry, so that other local threads will
235 block when attempting to enter a closure already under evaluation.
238 #if defined(CONCURRENT)
239 #define UPD_BH_UPDATABLE(heapptr) UPD_BH(heapptr,BH_UPD_info)
241 #define UPD_BH_UPDATABLE(heapptr) /* nothing -- BHed by GC */
244 #define UPD_BH_SINGLE_ENTRY(heapptr) UPD_BH(heapptr,BH_SINGLE_info)
245 /* BHed on entry -- GC cant do it */
248 %************************************************************************
250 \subsubsection[caf-update]{Entering CAFs}
252 %************************************************************************
254 When we enter a CAF, we update it with an indirection to a
255 heap-allocated black hole. The @UPD_CAF@ macro updates the CAF with an
256 @CAF@ indirection to the heap-allocated closure and adds the updated
257 CAF to the list of CAFs. It is up to the entry code to allocate the
260 The @CAF@ info table used is the @Caf_info@ table. It will be
261 overwritten at the start of garbage collection with the @Caf_Evac_Upd@
262 and then reset to @Caf_info@ during garbage collection.
264 In the parallel case, the new black hole will be a local node
265 (with a GA of 0). This means that the code to update indirections
266 does not need to check whether it's updating a CAF: the situation
267 simply never arises! If you change how this code works (e.g. to
268 update CAFs across the parallel machine), you should check @UPD_IND@
272 EXTDATA_RO(Caf_info);
275 #define UPD_CAF(cafptr, bhptr) \
277 SET_INFO_PTR(cafptr, Caf_info); \
278 IND_CLOSURE_PTR(cafptr) = (W_) (bhptr); \
279 IND_CLOSURE_LINK(cafptr) = (W_) StorageMgrInfo.CAFlist; \
280 StorageMgrInfo.CAFlist = (P_) (cafptr); \
285 %************************************************************************
287 \subsection[updating-closures]{Updating Closures}
289 %************************************************************************
291 We provide three macros:
294 \item[@UPD_IND(updclosure, heapptr)@]\ \\
295 Overwrites the updatable closure @updclosure@ with an indirection to
298 \item[@UPD_INPLACE_NOPTRS(updclosure, livemask)@]\ \\
299 This prepares the closure pointed to by @updclosure@ to be updated
300 in-place with a closure of size @MIN_UPD_SIZE@ containing no pointers.
302 \item[@UPD_INPLACE_PTRS(updclosure, livemask)@]\ \\
303 This prepares the closure pointed to by @updclosure@ to be updated
304 in-place with a closure of size @MIN_UPD_SIZE@ which may contain
305 pointers. It checks whether @updclosure@ is allowed to be updated
306 inplace. If it is not it:
308 \item Allocates space for a new closure of size @MIN_UPD_SIZE@ (by
309 calling @HEAP_CHK_RETRY@);
310 \item Overwrites @updclosure@ with an indirection to this new closure;
311 \item Modifies @updclosure@ to point to the newly-allocated closure.
314 All the macros ensure that @updclosure@ points to a closure of size
315 @MIN_UPD_SIZE@ ready to be filled in with the result of the update.
317 The code following @UPDATE_INPLACE@ is responsible for filling it in.
318 This requires the header to be set, with @INPLACE_UPD_HDR@, and the
319 body to be filled out.
322 The @UPD_IND@ and @UPDATE_INPLACE@ macros may have different
323 definitions depending on the garbage collection schemes in use.
325 Before describing the update macros we declare the partial application
326 entry and update code (See \tr{StgUpdate.lhc}).
329 EXTDATA_RO(PAP_info);
334 %************************************************************************
336 \subsubsection[updates-standard]{Implementation of Standard Updates}
338 %************************************************************************
343 /* In the concurrent world, the targed of an update might
344 be a black hole with a blocking queue attached. If so,
345 it will already be on the mutables list, and we have to be careful
346 not to put it on twice else it screws up the list. */
347 #define ALREADY_LINKED(closure) \
348 (IS_MUTABLE(INFO_PTR(closure)) && MUT_LINK(closure) != MUT_NOT_LINKED)
351 P_ AwakenBlockingQueue PROTO((P_));
353 void AwakenBlockingQueue PROTO((P_));
357 # define AWAKEN_BQ(updatee) \
358 do { if (IS_BQ_CLOSURE(updatee)) \
359 STGCALL1(void,(void *, P_), AwakenBlockingQueue, (P_) BQ_ENTRIES(updatee)); \
364 # define AWAKEN_BQ(updatee) \
365 do { if (IS_BQ_CLOSURE(updatee)) \
366 AwakenBlockingQueue((P_)BQ_ENTRIES(updatee)); \
370 # define AWAKEN_INPLACE_BQ()
372 #else /* !CONCURRENT */
374 # define ALREADY_LINKED(closure) 0 /* NB: see note above in CONCURRENT */
376 # define AWAKEN_BQ(updatee)
377 # define AWAKEN_INPLACE_BQ()
379 #endif /* CONCURRENT */
381 EXTDATA_RO(Ind_info);
384 # define Ind_info_TO_USE Ind_info
386 EXTDATA_RO(Perm_Ind_info);
387 EXTFUN(Perm_Ind_entry);
389 # define Ind_info_TO_USE ((AllFlags.doUpdEntryCounts) ? Perm_Ind_info : Ind_info)
392 #if defined(GC2s) || defined(GC1s) || defined(GCdu)
394 #define INPLACE_UPD_HDR(closure,infolbl,cc,size,ptrs) \
395 UPD_FIXED_HDR(closure,infolbl,cc)
397 #define UPD_IND(updclosure, heapptr) \
398 UPDATED_SET_UPDATED(updclosure); /* ticky */ \
399 AWAKEN_BQ(updclosure); \
400 SET_INFO_PTR(updclosure, Ind_info_TO_USE); \
401 IND_CLOSURE_PTR(updclosure) = (W_)(heapptr)
403 #define UPD_INPLACE_NOPTRS(livemask) \
404 UPDATED_SET_UPDATED(Node); /* ticky */ \
407 #define UPD_INPLACE_PTRS(livemask) \
408 UPDATED_SET_UPDATED(Node); /* ticky */ \
412 %************************************************************************
414 \subsubsection[updates-appel]{Implementation of Appel's Updates}
416 %************************************************************************
418 Appel's updates require the identification of old generation closures
419 which are updated. They must be updated with an indirection and linked
420 onto the list of old generation closures.
423 #else /* !(2s/1s/du) */
424 # if defined(GCap) || defined(GCgn)
427 #define INPLACE_UPD_HDR(closure,infolbl,cc,size,ptrs) \
428 UPD_FIXED_HDR(closure,infolbl,cc)
430 /* updclosure is the updatee, heapptr is what to update it with */
431 #define UPD_IND(updclosure, heapptr) \
432 { UPDATED_SET_UPDATED(updclosure); /* ticky */ \
433 if ( ((P_)(updclosure)) > StorageMgrInfo.OldLim ) { \
434 UPD_NEW_IND(); /*ticky*/ \
436 UPD_OLD_IND(); /*ticky*/ \
437 if(!ALREADY_LINKED(updclosure)) { \
438 MUT_LINK(updclosure) = (W_) StorageMgrInfo.OldMutables; \
439 StorageMgrInfo.OldMutables = (P_) (updclosure); \
442 AWAKEN_BQ(updclosure); \
443 SET_INFO_PTR(updclosure, Ind_info_TO_USE); \
444 IND_CLOSURE_PTR(updclosure) = (W_)(heapptr); \
448 * In threaded-land, we have to do the same nonsense as UPD_INPLACE_PTRS if
449 * we were a blocking queue on the old mutables list.
451 #define UPD_INPLACE_NOPTRS(live_regs_mask) \
452 UPDATED_SET_UPDATED(Node); /* ticky */ \
453 if ( Node > StorageMgrInfo.OldLim) { \
454 UPD_NEW_IN_PLACE_NOPTRS(); /*ticky*/ \
457 UPD_OLD_IN_PLACE_NOPTRS(); /*ticky*/ \
458 if(ALREADY_LINKED(Node)) { \
459 /* We are already on the old mutables list, so we \
460 can't update in place any more */ \
461 HEAP_CHK(live_regs_mask, _FHS+MIN_UPD_SIZE, 0); \
462 /* ticky-ticky (NB: was ALLOC_UPD_CON) */ \
463 ALLOC_CON(_FHS,1,MIN_UPD_SIZE-1,_FHS+MIN_UPD_SIZE); \
464 CC_ALLOC(CCC,_FHS+MIN_UPD_SIZE,CON_K); \
465 /* must awaken after any possible GC */ \
467 SET_INFO_PTR(Node, Ind_info_TO_USE); \
468 IND_CLOSURE_PTR(Node) = (W_)(Hp-(_FHS+MIN_UPD_SIZE-1)); \
469 Node = Hp-(_FHS+MIN_UPD_SIZE-1); \
473 #define UPD_INPLACE_PTRS(live_regs_mask) \
474 UPDATED_SET_UPDATED(Node); /* ticky */ \
475 if ( Node > StorageMgrInfo.OldLim) { \
476 UPD_NEW_IN_PLACE_PTRS(); /*ticky*/ \
479 /* redirect update with indirection */ \
480 UPD_OLD_IN_PLACE_PTRS(); /*ticky*/ \
482 HEAP_CHK(live_regs_mask, _FHS+MIN_UPD_SIZE, 0); \
483 /* ticky-ticky (NB: was ALLOC_UPD_CON) */ \
484 ALLOC_CON(_FHS,1,MIN_UPD_SIZE-1,_FHS+MIN_UPD_SIZE); \
485 CC_ALLOC(CCC,_FHS+MIN_UPD_SIZE,CON_K); \
487 if (!ALREADY_LINKED(Node)) { \
488 MUT_LINK(Node) = (W_) StorageMgrInfo.OldMutables; \
489 StorageMgrInfo.OldMutables = (P_) (Node); \
491 /* must awaken after any possible GC */ \
493 SET_INFO_PTR(Node, Ind_info_TO_USE); \
494 IND_CLOSURE_PTR(Node) = (W_)(Hp-(_FHS+MIN_UPD_SIZE-1)); \
495 Node = Hp-(_FHS+MIN_UPD_SIZE-1); \
497 # endif /* GCap || GCgn */
501 %************************************************************************
503 \subsection[freezing-arrays]{Changing Mutable Pointer closures into Immutable Closures}
505 %************************************************************************
507 When freezing an array of pointers we change the info table to
508 indicate it is now immutable to the garbage collector. The array will
509 be removed from the old generation mutable array list by the garbage\
512 This is only required for generational garbage collectors but we always
513 do it so better profiling information is provided.
516 #ifdef GC_MUT_REQUIRED
517 #define FREEZE_MUT_HDR(freezeclosure,immutinfo) \
518 SET_INFO_PTR(freezeclosure, immutinfo)
520 #define FREEZE_MUT_HDR(freezeclosure,immutinfo) \
521 SET_INFO_PTR(freezeclosure, immutinfo)
524 #endif /* SMUPDATE_H */