2 % (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
4 % $Id: CgHeapery.lhs,v 1.35 2002/12/11 15:36:26 simonmar Exp $
6 \section[CgHeapery]{Heap management functions}
10 funEntryChecks, altHeapCheck, unbxTupleHeapCheck, thunkChecks,
11 allocDynClosure, inPlaceAllocDynClosure
13 -- new functions, basically inserting macro calls into Code -- HWL
14 ,fetchAndReschedule, yield
17 #include "HsVersions.h"
23 import CgStackery ( getFinalStackHW )
24 import AbsCUtils ( mkAbstractCs, getAmodeRep )
25 import CgUsages ( getVirtAndRealHp, getRealSp, setVirtHp, setRealHp,
28 import ClosureInfo ( closureSize, closureGoodStuffSize,
29 slopSize, allocProfilingMsg, ClosureInfo
31 import PrimRep ( PrimRep(..), isFollowableRep )
32 import CmdLineOpts ( opt_GranMacros )
36 import PprAbsC ( pprMagicId ) -- tmp
42 %************************************************************************
44 \subsection[CgHeapery-heap-overflow]{Heap overflow checking}
46 %************************************************************************
48 The new code for heapChecks. For GrAnSim the code for doing a heap check
49 and doing a context switch has been separated. Especially, the HEAP_CHK
50 macro only performs a heap check. THREAD_CONTEXT_SWITCH should be used for
51 doing a context switch. GRAN_FETCH_AND_RESCHEDULE must be put at the
52 beginning of every slow entry code in order to simulate the fetching of
53 closures. If fetching is necessary (i.e. current closure is not local) then
54 an automatic context switch is done.
56 -----------------------------------------------------------------------------
57 A heap/stack check at a function or thunk entry point.
60 funEntryChecks :: Maybe CLabel -> AbstractC -> Code -> Code
61 funEntryChecks closure_lbl reg_save_code code
62 = hpStkCheck closure_lbl True reg_save_code code
64 thunkChecks :: Maybe CLabel -> Code -> Code
65 thunkChecks closure_lbl code
66 = hpStkCheck closure_lbl False AbsCNop code
69 :: Maybe CLabel -- function closure
70 -> Bool -- is a function? (not a thunk)
71 -> AbstractC -- register saves
75 hpStkCheck closure_lbl is_fun reg_save_code code
76 = getFinalStackHW (\ spHw ->
77 getRealSp `thenFC` \ sp ->
78 let stk_words = spHw - sp in
79 initHeapUsage (\ hHw ->
81 getTickyCtrLabel `thenFC` \ ticky_ctr ->
83 absC (checking_code stk_words hHw ticky_ctr) `thenC`
90 | Just lbl <- closure_lbl = CAssign nodeReg (CLbl lbl PtrRep)
93 save_code = mkAbstractCs [node_asst, reg_save_code]
95 checking_code stk hp ctr
98 then do_checks_fun stk hp save_code
99 else do_checks_np stk hp save_code,
102 else profCtrAbsC FSLIT("TICK_ALLOC_HEAP")
103 [ mkIntCLit hp, CLbl ctr DataPtrRep ]
110 :: Int -- stack headroom
111 -> Int -- heap headroom
112 -> AbstractC -- assignments to perform on failure
114 do_checks_fun 0 0 _ = AbsCNop
115 do_checks_fun 0 hp_words assts =
116 CCheck HP_CHK_FUN [ mkIntCLit hp_words ] assts
117 do_checks_fun stk_words 0 assts =
118 CCheck STK_CHK_FUN [ mkIntCLit stk_words ] assts
119 do_checks_fun stk_words hp_words assts =
120 CCheck HP_STK_CHK_FUN [ mkIntCLit stk_words, mkIntCLit hp_words ] assts
125 :: Int -- stack headroom
126 -> Int -- heap headroom
127 -> AbstractC -- assignments to perform on failure
129 do_checks_np 0 0 _ = AbsCNop
130 do_checks_np 0 hp_words assts =
131 CCheck HP_CHK_NP [ mkIntCLit hp_words ] assts
132 do_checks_np stk_words 0 assts =
133 CCheck STK_CHK_NP [ mkIntCLit stk_words ] assts
134 do_checks_np stk_words hp_words assts =
135 CCheck HP_STK_CHK_NP [ mkIntCLit stk_words, mkIntCLit hp_words ] assts
138 Heap checks in a case alternative are nice and easy, provided this is
139 a bog-standard algebraic case. We have in our hand:
141 * one return address, on the stack,
142 * one return value, in Node.
144 the canned code for this heap check failure just pushes Node on the
145 stack, saying 'EnterGHC' to return. The scheduler will return by
146 entering the top value on the stack, which in turn will return through
147 the return address, getting us back to where we were. This is
148 therefore only valid if the return value is *lifted* (just being
149 boxed isn't good enough).
151 For primitive returns, we have an unlifted value in some register
152 (either R1 or FloatReg1 or DblReg1). This means using specialised
153 heap-check code for these cases.
155 For unboxed tuple returns, there are an arbitrary number of possibly
156 unboxed return values, some of which will be in registers, and the
157 others will be on the stack, with gaps left for tagging the unboxed
158 objects. If a heap check is required, we need to fill in these tags.
160 The code below will cover all cases for the x86 architecture (where R1
161 is the only VanillaReg ever used). For other architectures, we'll
162 have to do something about saving and restoring the other registers.
166 :: Bool -- do not enter node on return
167 -> [MagicId] -- live registers
168 -> Code -- continuation
172 -- normal algebraic and primitive case alternatives:
174 altHeapCheck no_enter regs code
175 = initHeapUsage (\ hHw -> do_heap_chk hHw `thenC` code)
177 do_heap_chk :: HeapOffset -> Code
178 do_heap_chk words_required
179 = getTickyCtrLabel `thenFC` \ ctr ->
180 absC ( if words_required == 0
184 profCtrAbsC FSLIT("TICK_ALLOC_HEAP")
185 [ mkIntCLit words_required, CLbl ctr DataPtrRep ]
188 setRealHp words_required
191 non_void_regs = filter (/= VoidReg) regs
194 case non_void_regs of
196 -- No regs live: probably a Void return
198 CCheck HP_CHK_NOREGS [mkIntCLit words_required] AbsCNop
201 -- R1 is boxed, but unlifted: DO NOT enter R1 when we return.
202 | isFollowableRep rep && no_enter ->
203 CCheck HP_CHK_UNPT_R1 [mkIntCLit words_required] AbsCNop
205 -- R1 is lifted (the common case)
206 | isFollowableRep rep ->
208 [mkIntCLit words_required]
213 CCheck HP_CHK_UNBX_R1 [mkIntCLit words_required] AbsCNop
217 CCheck HP_CHK_F1 [mkIntCLit words_required] AbsCNop
221 CCheck HP_CHK_D1 [mkIntCLit words_required] AbsCNop
225 CCheck HP_CHK_L1 [mkIntCLit words_required] AbsCNop
228 _ -> panic ("CgHeapery.altHeapCheck: unimplemented heap-check, live regs = " ++ showSDoc (sep (map pprMagicId non_void_regs)))
231 -- unboxed tuple alternatives and let-no-escapes (the two most annoying
232 -- constructs to generate code for!):
235 :: [MagicId] -- live registers
236 -> Int -- no. of stack slots containing ptrs
237 -> Int -- no. of stack slots containing nonptrs
238 -> AbstractC -- code to insert in the failure path
242 unbxTupleHeapCheck regs ptrs nptrs fail_code code
243 -- we can't manage more than 255 pointers/non-pointers in a generic
245 | ptrs > 255 || nptrs > 255 = panic "altHeapCheck"
246 | otherwise = initHeapUsage (\ hHw -> do_heap_chk hHw `thenC` code)
248 do_heap_chk words_required
249 = getTickyCtrLabel `thenFC` \ ctr ->
250 absC ( if words_required == 0
254 profCtrAbsC FSLIT("TICK_ALLOC_HEAP")
255 [ mkIntCLit words_required, CLbl ctr DataPtrRep ]
258 setRealHp words_required
262 let liveness = mkRegLiveness regs ptrs nptrs
264 CCheck HP_CHK_UNBX_TUPLE
265 [mkIntCLit words_required,
266 mkIntCLit (I# (word2Int# liveness))]
269 -- build up a bitmap of the live pointer registers
271 #if __GLASGOW_HASKELL__ >= 503
272 shiftL = uncheckedShiftL#
277 mkRegLiveness :: [MagicId] -> Int -> Int -> Word#
278 mkRegLiveness [] (I# ptrs) (I# nptrs) =
279 (int2Word# nptrs `shiftL` 16#) `or#` (int2Word# ptrs `shiftL` 24#)
280 mkRegLiveness (VanillaReg rep i : regs) ptrs nptrs | isFollowableRep rep
281 = ((int2Word# 1#) `shiftL` (i -# 1#)) `or#` mkRegLiveness regs ptrs nptrs
282 mkRegLiveness (_ : regs) ptrs nptrs = mkRegLiveness regs ptrs nptrs
284 -- The two functions below are only used in a GranSim setup
285 -- Emit macro for simulating a fetch and then reschedule
287 fetchAndReschedule :: [MagicId] -- Live registers
288 -> Bool -- Node reqd?
291 fetchAndReschedule regs node_reqd =
292 if (node `elem` regs || node_reqd)
293 then fetch_code `thenC` reschedule_code
296 liveness_mask = mkRegLiveness regs 0 0
297 reschedule_code = absC (CMacroStmt GRAN_RESCHEDULE [
298 mkIntCLit (I# (word2Int# liveness_mask)),
299 mkIntCLit (if node_reqd then 1 else 0)])
301 --HWL: generate GRAN_FETCH macro for GrAnSim
302 -- currently GRAN_FETCH and GRAN_FETCH_AND_RESCHEDULE are miai
303 fetch_code = absC (CMacroStmt GRAN_FETCH [])
306 The @GRAN_YIELD@ macro is taken from JSM's code for Concurrent Haskell. It
307 allows to context-switch at places where @node@ is not alive (it uses the
308 @Continue@ rather than the @EnterNodeCode@ function in the RTS). We emit
309 this kind of macro at the beginning of the following kinds of basic bocks:
311 \item Slow entry code where node is not alive (see @CgClosure.lhs@). Normally
312 we use @fetchAndReschedule@ at a slow entry code.
313 \item Fast entry code (see @CgClosure.lhs@).
314 \item Alternatives in case expressions (@CLabelledCode@ structures), provided
315 that they are not inlined (see @CgCases.lhs@). These alternatives will
316 be turned into separate functions.
320 yield :: [MagicId] -- Live registers
321 -> Bool -- Node reqd?
324 yield regs node_reqd =
325 if opt_GranMacros && node_reqd
329 liveness_mask = mkRegLiveness regs 0 0
331 absC (CMacroStmt GRAN_YIELD
332 [mkIntCLit (I# (word2Int# liveness_mask))])
335 %************************************************************************
337 \subsection[initClosure]{Initialise a dynamic closure}
339 %************************************************************************
341 @allocDynClosure@ puts the thing in the heap, and modifies the virtual Hp
347 -> CAddrMode -- Cost Centre to stick in the object
348 -> CAddrMode -- Cost Centre to blame for this alloc
349 -- (usually the same; sometimes "OVERHEAD")
351 -> [(CAddrMode, VirtualHeapOffset)] -- Offsets from start of the object
352 -- ie Info ptr has offset zero.
353 -> FCode VirtualHeapOffset -- Returns virt offset of object
355 allocDynClosure closure_info use_cc blame_cc amodes_with_offsets
356 = getVirtAndRealHp `thenFC` \ (virtHp, realHp) ->
358 -- FIND THE OFFSET OF THE INFO-PTR WORD
359 -- virtHp points to last allocated word, ie 1 *before* the
360 -- info-ptr word of new object.
361 let info_offset = virtHp + 1
363 -- do_move IS THE ASSIGNMENT FUNCTION
364 do_move (amode, offset_from_start)
365 = CAssign (CVal (hpRel realHp
366 (info_offset + offset_from_start))
370 -- SAY WHAT WE ARE ABOUT TO DO
371 profCtrC (allocProfilingMsg closure_info)
372 [mkIntCLit (closureGoodStuffSize closure_info),
373 mkIntCLit slop_size] `thenC`
376 absC ( mkAbstractCs (
377 [ CInitHdr closure_info
378 (CAddr (hpRel realHp info_offset))
379 use_cc closure_size ]
380 ++ (map do_move amodes_with_offsets))) `thenC`
382 -- BUMP THE VIRTUAL HEAP POINTER
383 setVirtHp (virtHp + closure_size) `thenC`
385 -- RETURN PTR TO START OF OBJECT
388 closure_size = closureSize closure_info
389 slop_size = slopSize closure_info
392 Occasionally we can update a closure in place instead of allocating
393 new space for it. This is the function that does the business, assuming:
395 - node points to the closure to be overwritten
397 - the new closure doesn't contain any pointers if we're
398 using a generational collector.
401 inPlaceAllocDynClosure
403 -> CAddrMode -- Pointer to beginning of closure
404 -> CAddrMode -- Cost Centre to stick in the object
406 -> [(CAddrMode, VirtualHeapOffset)] -- Offsets from start of the object
407 -- ie Info ptr has offset zero.
410 inPlaceAllocDynClosure closure_info head use_cc amodes_with_offsets
411 = let -- do_move IS THE ASSIGNMENT FUNCTION
412 do_move (amode, offset_from_start)
413 = CAssign (CVal (CIndex head (mkIntCLit offset_from_start) WordRep)
418 absC ( mkAbstractCs (
419 [ CInitHdr closure_info head use_cc 0{-no alloc-} ]
420 ++ (map do_move amodes_with_offsets)))