2 % (c) The GRASP/AQUA Project, Glasgow University, 1998
5 Run through the STG code and compute the Static Reference Table for
6 each let-binding. At the same time, we figure out which top-level
7 bindings have no CAF references, and record the fact in their IdInfo.
12 import Id ( Id, setIdCafInfo, getIdCafInfo, externallyVisibleId,
15 import IdInfo ( CafInfo(..) )
23 computeSRTs :: [StgBinding] -> [(StgBinding,[Id])]
24 computeSRTs binds = srtBinds emptyUFM binds
28 srtBinds :: UniqFM CafInfo -> [StgBinding] -> [(StgBinding,[Id])]
31 srtTopBind rho b =: \(b, srt, rho) ->
32 (b,srt) : srtBinds rho bs
35 -----------------------------------------------------------------------------
36 Circular algorithm for simultaneously figuring out CafInfo and SRT
39 Our functions have type
41 :: UniqFM CafInfo -- which top-level ids don't refer to any CAfs
42 -> SrtOffset -- next free offset within the SRT
43 {- * -} -> StgExpr -- expression to analyse
45 -> (StgExpr, -- (e) newly annotated expression
46 UniqSet Id, -- (g) set of *all* global references
47 [Id], -- (s) SRT required for this expression
48 SrtOffset) -- (o) new offset
50 (g) is a set containing all local top-level and imported ids referred
51 to by the expression (e).
53 The set of all global references is used to build the environment,
54 which is passed in again. The environment is used to build the final
57 We build a single SRT for a recursive binding group, which is why the
58 SRT building is done at the binding level rather than the
61 Hence, the only argument which we can look at before returning is the
62 expression (marked with {- * -} above).
64 The SRT is built up in reverse order, to avoid too many expensive
65 appends. We therefore reverse the SRT before returning it, so that
66 the offsets will be from the beginning of the SRT.
68 -----------------------------------------------------------------------------
71 The environment contains a mapping from local top-level bindings to
72 CafInfo. The CafInfo is either
74 NoCafRefs - indicating that the id is not a CAF and furthermore
75 that it doesn't refer, even indirectly, to any CAFs.
77 MayHaveCafRefs - everything else.
79 A function whose CafInfo is NoCafRefs will have an empty SRT, and its
80 closure will not appear in the SRT of any other function (unless we're
81 compiling without optimisation and the CafInfos haven't been emitted
82 in the interface files).
84 Top-Level recursive groups
86 This gets a bit complicated, but the general idea is that we want a
87 single SRT for the whole group, and we'd rather not have recursive
88 references in it if at all possible.
90 We collect all the global references for the group, and filter out
91 those that are binders in the group and not CAFs themselves. This set
92 of references is then used to infer the CafInfo for each of the
93 binders in the group. Why is it done this way?
95 - if all the bindings in the group just refer to each other,
96 and none of them are CAFs, we'd like to get an empty SRT.
98 - if any of the bindings in the group refer to a CAF, this will
101 Hmm, that probably makes no sense.
107 -> (StgBinding, -- the new binding
108 [Id], -- the SRT for this binding
109 UniqFM CafInfo) -- the new environment
111 srtTopBind rho (StgNonRec binder rhs) =
113 -- no need to use circularity for non-recursive bindings
114 srtRhs rho 0{-initial offset-} rhs =: \(rhs, g, srt, off) ->
116 filtered_g = filter (mayHaveCafRefs rho) (uniqSetToList g)
117 caf_info = mk_caf_info rhs filtered_g
118 binder' = setIdCafInfo binder caf_info
119 rho' = addToUFM rho binder' caf_info
120 extra_refs = filter (`notElem` srt) filtered_g
121 bind_srt = reverse (extra_refs ++ srt)
124 StgRhsClosure _ _ _ _ _ _ _ ->
125 (StgNonRec binder' (attach_srt_rhs rhs 0 (length bind_srt)),
128 -- don't output an SRT for the constructor, but just remember
129 -- whether it had any caf references or not.
130 StgRhsCon _ _ _ -> (StgNonRec binder' rhs, [], rho')
133 srtTopBind rho (StgRec bs) =
134 (attach_srt_bind (StgRec (reverse new_bs')) 0 (length bind_srt),
137 (binders,rhss) = unzip bs
139 non_caf_binders = [ b | (b, rhs) <- bs, not (caf_rhs rhs) ]
141 -- circular: rho' is calculated from g below
142 (new_bs, g, srt, _) = doBinds bs [] emptyUniqSet [] 0
144 -- filter out ourselves from the global references: it makes no
145 -- sense to refer recursively to our SRT unless the recursive
146 -- reference is required by a nested SRT.
147 filtered_g = filter (\id -> id `notElem` non_caf_binders &&
148 mayHaveCafRefs rho id) (uniqSetToList g)
149 extra_refs = filter (`notElem` srt) filtered_g
150 bind_srt = reverse (extra_refs ++ srt)
151 caf_infos = map (\rhs -> mk_caf_info rhs filtered_g) rhss
152 rho' = addListToUFM rho (zip binders caf_infos)
153 binders' = zipWith setIdCafInfo binders caf_infos
155 new_bs' = zip binders' (map snd new_bs)
157 doBinds [] new_binds g srt off = (reverse new_binds, g, srt, off)
158 doBinds ((binder,rhs):binds) new_binds g srt off =
159 srtRhs rho' off rhs =: \(rhs, rhs_g, rhs_srt, off) ->
161 g' = unionUniqSets rhs_g g
162 srt' = rhs_srt ++ srt
164 doBinds binds ((binder,rhs):new_binds) g' srt' off
166 caf_rhs (StgRhsClosure _ _ _ free_vars _ [] body) = True
170 -----------------------------------------------------------------------------
171 Non-top-level bindings
174 srtBind :: UniqFM CafInfo -> Int -> StgBinding
175 -> (StgBinding, UniqSet Id, [Id], Int)
177 srtBind rho off (StgNonRec binder rhs) =
178 srtRhs rho off rhs =: \(rhs, g, srt, off) ->
179 (StgNonRec binder rhs, g, srt, off)
181 srtBind rho off (StgRec binds) =
182 (StgRec new_binds, g, srt, new_off)
184 -- process each binding
185 (new_binds, g, srt, new_off) = doBinds binds emptyUniqSet [] off []
187 doBinds [] g srt off new_binds = (reverse new_binds, g, srt, off)
188 doBinds ((binder,rhs):binds) g srt off new_binds =
189 srtRhs rho off rhs =: \(rhs, g', srt', off) ->
190 doBinds binds (unionUniqSets g g') (srt'++srt) off
191 ((binder,rhs):new_binds)
194 -----------------------------------------------------------------------------
198 srtRhs :: UniqFM CafInfo -> Int -> StgRhs
199 -> (StgRhs, UniqSet Id, [Id], Int)
201 srtRhs rho off (StgRhsClosure cc bi old_srt free_vars u args body) =
202 srtExpr rho off body =: \(body, g, srt, off) ->
203 (StgRhsClosure cc bi old_srt free_vars u args body, g, srt, off)
205 srtRhs rho off e@(StgRhsCon cc con args) =
206 (e, getGlobalRefs rho args, [], off)
209 -----------------------------------------------------------------------------
213 srtExpr :: UniqFM CafInfo -> Int -> StgExpr
214 -> (StgExpr, UniqSet Id, [Id], Int)
216 srtExpr rho off e@(StgApp f args) =
217 (e, getGlobalRefs rho (StgVarArg f:args), [], off)
219 srtExpr rho off e@(StgCon con args ty) =
220 (e, getGlobalRefs rho args, [], off)
222 srtExpr rho off (StgCase scrut live1 live2 uniq _{-srt-} alts) =
223 srtCaseAlts rho off alts =: \(alts, alts_g, alts_srt, alts_off) ->
225 extra_refs = filter (`notElem` alts_srt)
226 (filter (mayHaveCafRefs rho) (uniqSetToList alts_g))
227 this_srt = extra_refs ++ alts_srt
228 scrut_off = alts_off + length extra_refs
230 srtExpr rho scrut_off scrut =: \(scrut, scrut_g, scrut_srt, case_off) ->
232 g = unionUniqSets alts_g scrut_g
233 srt = scrut_srt ++ this_srt
234 srt_info = case length this_srt of
238 (StgCase scrut live1 live2 uniq srt_info alts, g, srt, case_off)
240 srtExpr rho off (StgLet bind body) =
241 srtLet rho off bind body StgLet
243 -- let-no-escapes are delicate, see below
244 srtExpr rho off (StgLetNoEscape live1 live2 bind body) =
245 srtLet rho off bind body (StgLetNoEscape live1 live2)
246 =: \(expr, g, srt, off') ->
248 -- find the SRT for the *whole* expression
250 all_srt | length == 0 = NoSRT
251 | otherwise = SRT off length
253 (fixLNE_srt all_srt expr, g, srt, off')
255 srtExpr rho off (StgSCC cc expr) =
256 srtExpr rho off expr =: \(expr, g, srt, off) ->
257 (StgSCC cc expr, g, srt, off)
260 -----------------------------------------------------------------------------
263 This is quite complicated stuff...
266 srtLet rho off bind body let_constr
268 -- If the bindings are all constructors, then we don't need to
269 -- buid an SRT at all...
270 | all_con_binds bind =
271 srtBind rho off bind =: \(bind, bind_g, bind_srt, off) ->
272 srtExpr rho off body =: \(body, body_g, body_srt, off) ->
274 g = unionUniqSets bind_g body_g
275 srt = body_srt ++ bind_srt
277 (let_constr bind body, g, srt, off)
279 -- we have some closure bindings...
282 -- first, find the sub-SRTs in the binding
283 srtBind rho off bind =: \(bind, bind_g, bind_srt, bind_off) ->
285 -- Construct the SRT for this binding from its sub-SRTs and any new global
286 -- references which aren't already contained in one of the sub-SRTs (and
287 -- which are "live").
289 extra_refs = filter (`notElem` bind_srt)
290 (filter (mayHaveCafRefs rho) (uniqSetToList bind_g))
291 this_srt = extra_refs ++ bind_srt
293 -- Add the length of the new entries to the
294 -- current offset to get the next free offset in the global SRT.
295 body_off = bind_off + length extra_refs
298 -- now find the SRTs in the body
299 srtExpr rho body_off body =: \(body, body_g, body_srt, let_off) ->
302 -- union all the global references together
303 let_g = unionUniqSets bind_g body_g
305 -- concatenate the sub-SRTs
306 let_srt = body_srt ++ this_srt
308 -- attach the SRT info to the binding
309 bind' = attach_srt_bind bind off (length this_srt)
311 (let_constr bind' body, let_g, let_srt, let_off)
314 -----------------------------------------------------------------------------
318 srtCaseAlts :: UniqFM CafInfo -> Int -> StgCaseAlts ->
319 (StgCaseAlts, UniqSet Id, [Id], Int)
321 srtCaseAlts rho off (StgAlgAlts t alts dflt) =
322 srtAlgAlts rho off alts [] emptyUniqSet []
323 =: \(alts, alts_g, alts_srt, off) ->
324 srtDefault rho off dflt =: \(dflt, dflt_g, dflt_srt, off) ->
326 g = unionUniqSets alts_g dflt_g
327 srt = dflt_srt ++ alts_srt
329 (StgAlgAlts t alts dflt, g, srt, off)
331 srtCaseAlts rho off (StgPrimAlts t alts dflt) =
332 srtPrimAlts rho off alts [] emptyUniqSet []
333 =: \(alts, alts_g, alts_srt, off) ->
334 srtDefault rho off dflt =: \(dflt, dflt_g, dflt_srt, off) ->
336 g = unionUniqSets alts_g dflt_g
337 srt = dflt_srt ++ alts_srt
339 (StgPrimAlts t alts dflt, g, srt, off)
341 srtAlgAlts rho off [] new_alts g srt = (reverse new_alts, g, srt, off)
342 srtAlgAlts rho off ((con,args,used,rhs):alts) new_alts g srt =
343 srtExpr rho off rhs =: \(rhs, rhs_g, rhs_srt, off) ->
345 g' = unionUniqSets rhs_g g
346 srt' = rhs_srt ++ srt
348 srtAlgAlts rho off alts ((con,args,used,rhs) : new_alts) g' srt'
350 srtPrimAlts rho off [] new_alts g srt = (reverse new_alts, g, srt, off)
351 srtPrimAlts rho off ((lit,rhs):alts) new_alts g srt =
352 srtExpr rho off rhs =: \(rhs, rhs_g, rhs_srt, off) ->
354 g' = unionUniqSets rhs_g g
355 srt' = rhs_srt ++ srt
357 srtPrimAlts rho off alts ((lit,rhs) : new_alts) g' srt'
359 srtDefault rho off StgNoDefault = (StgNoDefault,emptyUniqSet,[],off)
360 srtDefault rho off (StgBindDefault rhs) =
361 srtExpr rho off rhs =: \(rhs, g, srt, off) ->
362 (StgBindDefault rhs, g, srt, off)
365 -----------------------------------------------------------------------------
367 Decide whether a closure looks like a CAF or not. In an effort to
368 keep the number of CAFs (and hence the size of the SRTs) down, we
369 would also like to look at the expression and decide whether it
370 requires a small bounded amount of heap, so we can ignore it as a CAF.
371 In these cases, we need to use an additional CAF list to keep track of
372 non-collectable CAFs.
374 We mark real CAFs as `MayHaveCafRefs' because this information is used
375 to decide whether a particular closure needs to be referenced in an
380 :: StgRhs -- right-hand-side of the definition
381 -> [Id] -- static references
384 -- special case for expressions which are always bottom,
385 -- such as 'error "..."'. We don't need to record it as
386 -- a CAF, since it can only be entered once.
387 mk_caf_info (StgRhsClosure _ _ _ free_vars _ [] e) srt
388 | isBottomingExpr e && null srt = NoCafRefs
390 mk_caf_info (StgRhsClosure _ _ _ free_vars upd args body) srt
391 | isUpdatable upd = MayHaveCafRefs -- a real live CAF
392 | null srt = NoCafRefs -- function w/ no static references
393 | otherwise = MayHaveCafRefs -- function w/ some static references
395 mk_caf_info rcon@(StgRhsCon cc con args) srt
396 | null srt = NoCafRefs -- constructor w/ no static references
397 | otherwise = MayHaveCafRefs -- otherwise, treat as a CAF
400 isBottomingExpr (StgLet bind expr) = isBottomingExpr expr
401 isBottomingExpr (StgApp f args) = idAppIsBottom f (length args)
402 isBottomingExpr _ = False
405 -----------------------------------------------------------------------------
407 Here we decide which Id's to place in the static reference table. An
408 internal top-level id will be in the environment with the appropriate
409 CafInfo, so we use that if available. An imported top-level Id will
410 have the CafInfo attached. Otherwise, we just ignore the Id.
413 getGlobalRefs :: UniqFM CafInfo -> [StgArg] -> UniqSet Id
414 getGlobalRefs rho args = mkUniqSet (concat (map (globalRefArg rho) args))
416 globalRefArg :: UniqFM CafInfo -> StgArg -> [Id]
418 globalRefArg rho (StgVarArg id)
421 case lookupUFM rho id of {
422 Just _ -> [id]; -- can't look at the caf_info yet...
425 if externallyVisibleId id
426 then case getIdCafInfo id of
427 MayHaveCafRefs -> [id]
432 globalRefArg rho _ = []
436 mayHaveCafRefs rho id =
437 case lookupUFM rho id of
438 Just MayHaveCafRefs -> True
439 Just NoCafRefs -> False
443 -----------------------------------------------------------------------------
447 attach_srt_bind :: StgBinding -> Int -> Int -> StgBinding
448 attach_srt_bind (StgNonRec binder rhs) off len =
449 StgNonRec binder (attach_srt_rhs rhs off len)
450 attach_srt_bind (StgRec binds) off len =
451 StgRec [ (v,attach_srt_rhs rhs off len) | (v,rhs) <- binds ]
453 attach_srt_rhs :: StgRhs -> Int -> Int -> StgRhs
454 attach_srt_rhs (StgRhsCon cc con args) off length
455 = StgRhsCon cc con args
456 attach_srt_rhs (StgRhsClosure cc bi _ free upd args rhs) off length
457 = StgRhsClosure cc bi srt free upd args rhs
459 srt | length == 0 = NoSRT
460 | otherwise = SRT off length
463 all_con_binds (StgNonRec x rhs) = con_rhs rhs
464 all_con_binds (StgRec bs) = all con_rhs (map snd bs)
466 con_rhs (StgRhsCon _ _ _) = True
473 -----------------------------------------------------------------------------
474 Fix up the SRT's in a let-no-escape.
476 (for a description of let-no-escapes, see CgLetNoEscape.lhs)
478 Here's the problem: a let-no-escape isn't represented by an activation
479 record on the stack. It seems either very difficult or impossible to
480 get the liveness bitmap right in the info table, so we don't do it
481 this way (the liveness mask isn't constant).
483 So, the question is how does the garbage collector get access to the
484 SRT for the rhs of the let-no-escape? It can't see an info table, so
485 it must get the SRT from somewhere else. Here's an example:
487 let-no-escape x = .... f ....
489 p -> .... x ... g ....
491 (f and g are global). Suppose we garbage collect while evaluating
492 'blah'. The stack will contain an activation record for the case,
493 which will point to an SRT containing [g] (according to our SRT
494 algorithm above). But, since the case continuation can call x, and
495 hence f, the SRT should really be [f,g].
499 let-no-escape {-rec-} z = \x -> case blah of
505 if we GC while evaluating blah2, then the case continuation on the
506 stack needs to refer to [f] in its SRT, because we can reach f by
507 calling z recursively.
511 The following code fixes up a let-no-escape expression after we've run
512 the SRT algorithm. It needs to know the SRT for the *whole*
513 expression (this is plugged in instead of the SRT for case exprsesions
514 in the body). The good news is that we only need to traverse nested
515 case expressions, since the let-no-escape bound variable can't occur
516 in the rhs of a let or in a case scrutinee.
518 For recursive let-no-escapes, the body is processed as for
519 non-recursive let-no-escapes, but case expressions in the rhs of each
520 binding have their SRTs replaced with the SRT for the binding group
521 (*not* the SRT of the whole let-no-escape expression).
524 fixLNE_srt :: SRT -> StgExpr -> StgExpr
525 fixLNE_srt all_srt (StgLetNoEscape live1 live2 (StgNonRec id rhs) body)
526 = StgLetNoEscape live1 live2 (StgNonRec id rhs) (fixLNE [id] all_srt body)
528 fixLNE_srt all_srt (StgLetNoEscape live1 live2 (StgRec pairs) body)
529 = StgLetNoEscape live1 live2
530 (StgRec (map fixLNE_rec pairs)) (fixLNE binders all_srt body)
532 binders = map fst pairs
533 fixLNE_rec (id,StgRhsClosure cc bi srt fvs uf args e) =
534 (id, StgRhsClosure cc bi srt fvs uf args (fixLNE binders srt e))
535 fixLNE_rec (id,con) = (id,con)
537 fixLNE :: [Id] -> SRT -> StgExpr -> StgExpr
539 fixLNE ids srt expr@(StgCase scrut live rhs_live bndr old_srt alts)
540 | any (`elementOfUniqSet` rhs_live) ids
541 = StgCase scrut live rhs_live bndr srt (fixLNE_alts ids srt alts)
543 -- can't be in the scrutinee, because it's a let-no-escape!
545 fixLNE ids srt expr@(StgLetNoEscape live rhs_live bind body)
546 | any (`elementOfUniqSet` rhs_live) ids =
547 StgLetNoEscape live rhs_live (fixLNE_bind ids srt bind)
548 (fixLNE ids srt body)
549 | any (`elementOfUniqSet` live) ids =
550 StgLetNoEscape live rhs_live bind (fixLNE ids srt body)
553 fixLNE ids srt (StgLet bind body) = StgLet bind (fixLNE ids srt body)
554 fixLNE ids srt (StgSCC cc expr) = StgSCC cc (fixLNE ids srt expr)
555 fixLNE ids srt expr = expr
557 fixLNE_alts ids srt (StgAlgAlts t alts dflt)
558 = StgAlgAlts t (map (fixLNE_algalt ids srt) alts) (fixLNE_dflt ids srt dflt)
560 fixLNE_alts ids srt (StgPrimAlts t alts dflt)
561 = StgPrimAlts t (map (fixLNE_primalt ids srt) alts) (fixLNE_dflt ids srt dflt)
563 fixLNE_algalt ids srt (con,args,used,rhs) = (con,args,used, fixLNE ids srt rhs)
564 fixLNE_primalt ids srt (lit,rhs) = (lit, fixLNE ids srt rhs)
566 fixLNE_dflt ids srt (StgNoDefault) = StgNoDefault
567 fixLNE_dflt ids srt (StgBindDefault rhs) = StgBindDefault (fixLNE ids srt rhs)
569 fixLNE_bind ids srt (StgNonRec bndr rhs)
570 = StgNonRec bndr (fixLNE_rhs ids srt rhs)
571 fixLNE_bind ids srt (StgRec pairs)
572 = StgRec [ (bndr, fixLNE_rhs ids srt rhs) | (bndr,rhs) <- pairs ]
574 fixLNE_rhs ids srt rhs@(StgRhsClosure cc bi old_srt fvs uf args expr)
575 | any (`elem` fvs) ids
576 = StgRhsClosure cc bi srt fvs uf args (fixLNE ids srt expr)
578 fixLNE_rhs ids srt rhs@(StgRhsCon cc con args) = rhs