1 #if __GLASGOW_HASKELL__ >= 611
2 {-# OPTIONS_GHC -XNoMonoLocalBinds #-}
4 -- Norman likes local bindings
5 -- If this module lives on I'd like to get rid of this flag in due course
8 ( SlotEnv, liveSlotAnal, liveSlotTransfers, removeLiveSlotDefs
9 , layout, manifestSP, igraph, areaBuilder
10 , stubSlotsOnDeath ) -- to help crash early during debugging
14 import Prelude hiding (zip, unzip, last)
23 import MkZipCfgCmm hiding (CmmBlock, CmmGraph)
26 import SMRep (ByteOff)
33 import qualified Data.Map as Map
34 import qualified FiniteMap as Map
36 ------------------------------------------------------------------------
38 ------------------------------------------------------------------------
40 -- | Before we lay out the stack, we need to know something about the
41 -- liveness of the stack slots. In particular, to decide whether we can
42 -- reuse a stack location to hold multiple stack slots, we need to know
43 -- when each of the stack slots is used.
44 -- Although tempted to use something simpler, we really need a full interference
45 -- graph. Consider the following case:
47 -- 1 -> <spill x>; // y is dead out
48 -- 2 -> <spill y>; // x is dead out
49 -- 3 -> <spill x and y>
50 -- If we consider the arms in order and we use just the deadness information given by a
51 -- dataflow analysis, we might decide to allocate the stack slots for x and y
52 -- to the same stack location, which will lead to incorrect code in the third arm.
53 -- We won't make this mistake with an interference graph.
55 -- First, the liveness analysis.
56 -- We represent a slot with an area, an offset into the area, and a width.
57 -- Tracking the live slots is a bit tricky because there may be loads and stores
58 -- into only a part of a stack slot (e.g. loading the low word of a 2-word long),
59 -- e.g. Slot A 0 8 overlaps with Slot A 4 4.
61 -- The definition of a slot set is intended to reduce the number of overlap
62 -- checks we have to make. There's no reason to check for overlap between
63 -- slots in different areas, so we segregate the map by Area's.
64 -- We expect few slots in each Area, so we collect them in an unordered list.
65 -- To keep these lists short, any contiguous live slots are coalesced into
66 -- a single slot, on insertion.
68 slotLattice :: DataflowLattice SubAreaSet
69 slotLattice = DataflowLattice "live slots" Map.empty add False
70 where add new old = case Map.foldRightWithKey addArea (False, old) new of
73 addArea a newSlots z = foldr (addSlot a) z newSlots
74 addSlot a slot (changed, map) =
75 let (c, live) = liveGen slot $ Map.findWithDefault [] a map
76 in (c || changed, Map.insert a live map)
78 type SlotEnv = BlockEnv SubAreaSet
79 -- The sub-areas live on entry to the block
81 type SlotFix a = FuelMonad (BackwardFixedPoint Middle Last SubAreaSet a)
83 liveSlotAnal :: LGraph Middle Last -> FuelMonad SlotEnv
84 liveSlotAnal g = liftM zdfFpFacts (res :: SlotFix ())
85 where res = zdfSolveFromL emptyBlockEnv "live slot analysis" slotLattice
86 liveSlotTransfers (fact_bot slotLattice) g
88 -- Add the subarea s to the subareas in the list-set (possibly coalescing it with
89 -- adjacent subareas), and also return whether s was a new addition.
90 liveGen :: SubArea -> [SubArea] -> (Bool, [SubArea])
91 liveGen s set = liveGen' s set []
92 where liveGen' s [] z = (True, s : z)
93 liveGen' s@(a, hi, w) (s'@(a', hi', w') : rst) z =
94 if a /= a' || hi < lo' || lo > hi' then -- no overlap
95 liveGen' s rst (s' : z)
96 else if s' `contains` s then -- old contains new
98 else -- overlap: coalesce the slots
99 let new_hi = max hi hi'
101 in liveGen' (a, new_hi, new_hi - new_lo) rst z
102 where lo = hi - w -- remember: areas grow down
104 contains (a, hi, w) (a', hi', w') =
105 a == a' && hi >= hi' && hi - w <= hi' - w'
107 liveKill :: SubArea -> [SubArea] -> [SubArea]
108 liveKill (a, hi, w) set = -- pprTrace "killing slots in area" (ppr a) $
110 where liveKill' [] z = z
111 liveKill' (s'@(a', hi', w') : rst) z =
112 if a /= a' || hi < lo' || lo > hi' then -- no overlap
113 liveKill' rst (s' : z)
114 else -- overlap: split the old slot
115 let z' = if hi' > hi then (a, hi', hi' - hi) : z else z
116 z'' = if lo > lo' then (a, lo, lo - lo') : z' else z'
118 where lo = hi - w -- remember: areas grow down
121 -- Note: the stack slots that hold variables returned on the stack are not
122 -- considered live in to the block -- we treat the first node as a definition site.
123 -- BEWARE?: Am I being a little careless here in failing to check for the
124 -- entry Id (which would use the CallArea Old).
125 liveSlotTransfers :: BackwardTransfers Middle Last SubAreaSet
127 BackwardTransfers first liveInSlots liveLastIn
128 where first id live = Map.delete (CallArea (Young id)) live
130 -- Slot sets: adding slots, removing slots, and checking for membership.
131 liftToArea :: Area -> ([SubArea] -> [SubArea]) -> SubAreaSet -> SubAreaSet
132 addSlot, removeSlot :: SubAreaSet -> SubArea -> SubAreaSet
133 elemSlot :: SubAreaSet -> SubArea -> Bool
134 liftToArea a f map = Map.insert a (f (Map.findWithDefault [] a map)) map
135 addSlot live (a, i, w) = liftToArea a (snd . liveGen (a, i, w)) live
136 removeSlot live (a, i, w) = liftToArea a (liveKill (a, i, w)) live
137 elemSlot live (a, i, w) =
138 not $ fst $ liveGen (a, i, w) (Map.findWithDefault [] a live)
140 removeLiveSlotDefs :: (DefinerOfSlots s, UserOfSlots s) => SubAreaSet -> s -> SubAreaSet
141 removeLiveSlotDefs = foldSlotsDefd removeSlot
143 liveInSlots :: (DefinerOfSlots s, UserOfSlots s) => s -> SubAreaSet -> SubAreaSet
144 liveInSlots x live = foldSlotsUsed addSlot (removeLiveSlotDefs live x) x
146 liveLastIn :: Last -> (BlockId -> SubAreaSet) -> SubAreaSet
147 liveLastIn l env = liveInSlots l (liveLastOut env l)
149 -- Don't forget to keep the outgoing parameters in the CallArea live,
150 -- as well as the update frame.
151 -- Note: We have to keep the update frame live at a call because of the
152 -- case where the function doesn't return -- in that case, there won't
153 -- be a return to keep the update frame live. We'd still better keep the
154 -- info pointer in the update frame live at any call site;
155 -- otherwise we could screw up the garbage collector.
156 liveLastOut :: (BlockId -> SubAreaSet) -> Last -> SubAreaSet
159 LastCall _ Nothing n _ _ ->
160 add_area (CallArea Old) n out -- add outgoing args (includes upd frame)
161 LastCall _ (Just k) n _ (Just _) ->
162 add_area (CallArea Old) n (add_area (CallArea (Young k)) n out)
163 LastCall _ (Just k) n _ Nothing ->
164 add_area (CallArea (Young k)) n out
166 where out = joinOuts slotLattice env l
167 add_area _ n live | n == 0 = live
169 Map.insert a (snd $ liveGen (a, n, n) $ Map.findWithDefault [] a live) live
171 -- The liveness analysis must be precise: otherwise, we won't know if a definition
172 -- should really kill a live-out stack slot.
173 -- But the interference graph does not have to be precise -- it might decide that
174 -- any live areas interfere. To maintain both a precise analysis and an imprecise
175 -- interference graph, we need to convert the live-out stack slots to graph nodes
176 -- at each and every instruction; rather than reconstruct a new list of nodes
177 -- every time, I provide a function to fold over the nodes, which should be a
178 -- reasonably efficient approach for the implementations we envision.
179 -- Of course, it will probably be much easier to program if we just return a list...
180 type Set x = Map x ()
181 data IGraphBuilder n =
182 Builder { foldNodes :: forall z. SubArea -> (n -> z -> z) -> z -> z
183 , _wordsOccupied :: AreaMap -> AreaMap -> n -> [Int]
186 areaBuilder :: IGraphBuilder Area
187 areaBuilder = Builder fold words
188 where fold (a, _, _) f z = f a z
189 words areaSize areaMap a =
190 case Map.lookup a areaMap of
191 Just addr -> [addr .. addr + (Map.lookup a areaSize `orElse`
192 pprPanic "wordsOccupied: unknown area" (ppr a))]
195 --slotBuilder :: IGraphBuilder (Area, Int)
196 --slotBuilder = undefined
198 -- Now, we can build the interference graph.
199 -- The usual story: a definition interferes with all live outs and all other
201 type IGraph x = Map x (Set x)
202 type IGPair x = (IGraph x, IGraphBuilder x)
203 igraph :: (Ord x) => IGraphBuilder x -> SlotEnv -> LGraph Middle Last -> IGraph x
204 igraph builder env g = foldr interfere Map.empty (postorder_dfs g)
205 where foldN = foldNodes builder
206 interfere block igraph =
207 let (h, l) = goto_end (unzip block)
208 --heads :: ZHead Middle -> (IGraph x, SubAreaSet) -> IGraph x
209 heads (ZFirst _) (igraph, _) = igraph
210 heads (ZHead h m) (igraph, liveOut) =
211 heads h (addEdges igraph m liveOut, liveInSlots m liveOut)
212 -- add edges between a def and the other defs and liveouts
213 addEdges igraph i out = fst $ foldSlotsDefd addDef (igraph, out) i
214 addDef (igraph, out) def@(a, _, _) =
215 (foldN def (addDefN out) igraph,
216 Map.insert a (snd $ liveGen def (Map.findWithDefault [] a out)) out)
217 addDefN out n igraph =
218 let addEdgeNO o igraph = foldN o addEdgeNN igraph
219 addEdgeNN n' igraph = addEdgeNN' n n' $ addEdgeNN' n' n igraph
220 addEdgeNN' n n' igraph = Map.insert n (Map.insert n' () set) igraph
221 where set = Map.findWithDefault Map.empty n igraph
222 in Map.foldRightWithKey (\ _ os igraph -> foldr addEdgeNO igraph os) igraph out
223 env' bid = lookupBlockEnv env bid `orElse` panic "unknown blockId in igraph"
224 in heads h $ case l of LastExit -> (igraph, Map.empty)
225 LastOther l -> (addEdges igraph l $ liveLastOut env' l,
228 -- Before allocating stack slots, we need to collect one more piece of information:
229 -- what's the highest offset (in bytes) used in each Area?
230 -- We'll need to allocate that much space for each Area.
231 getAreaSize :: ByteOff -> LGraph Middle Last -> AreaMap
232 -- The domain of the returned mapping consists only of Areas
233 -- used for (a) variable spill slots, and (b) parameter passing ares for calls
234 getAreaSize entry_off g@(LGraph _ _) =
235 fold_blocks (fold_fwd_block first add_regslots last)
236 (Map.singleton (CallArea Old) entry_off) g
238 last l@(LastOther (LastCall _ Nothing args res _)) z =
239 add_regslots l (add (add z area args) area res)
240 where area = CallArea Old
241 last l@(LastOther (LastCall _ (Just k) args res _)) z =
242 add_regslots l (add (add z area args) area res)
243 where area = CallArea (Young k)
244 last l z = add_regslots l z
245 add_regslots i z = foldSlotsUsed addSlot (foldSlotsDefd addSlot z i) i
246 addSlot z (a@(RegSlot (LocalReg _ ty)), _, _) =
247 add z a $ widthInBytes $ typeWidth ty
249 add z a off = Map.insert a (max off (Map.findWithDefault 0 a z)) z
250 -- The 'max' is important. Two calls, to f and g, might share a common
251 -- continuation (and hence a common CallArea), but their number of overflow
252 -- parameters might differ.
255 -- Find the Stack slots occupied by the subarea's conflicts
256 conflictSlots :: Ord x => IGPair x -> AreaMap -> AreaMap -> SubArea -> Set Int
257 conflictSlots (ig, Builder foldNodes wordsOccupied) areaSize areaMap subarea =
258 foldNodes subarea foldNode Map.empty
259 where foldNode n set = Map.foldRightWithKey conflict set $ Map.findWithDefault Map.empty n ig
260 conflict n' () set = liveInSlots areaMap n' set
261 -- Add stack slots occupied by igraph node n
262 liveInSlots areaMap n set = foldr setAdd set (wordsOccupied areaSize areaMap n)
263 setAdd w s = Map.insert w () s
265 -- Find any open space on the stack, starting from the offset.
266 -- If the area is a CallArea or a spill slot for a pointer, then it must
268 freeSlotFrom :: Ord x => IGPair x -> AreaMap -> Int -> AreaMap -> Area -> Int
269 freeSlotFrom ig areaSize offset areaMap area =
270 let size = Map.lookup area areaSize `orElse` 0
271 conflicts = conflictSlots ig areaSize areaMap (area, size, size)
272 -- CallAreas and Ptrs need to be word-aligned (round up!)
273 align = case area of CallArea _ -> align'
274 RegSlot r | isGcPtrType (localRegType r) -> align'
276 align' n = (n + (wORD_SIZE - 1)) `div` wORD_SIZE * wORD_SIZE
277 -- Find a space big enough to hold the area
278 findSpace curr 0 = curr
279 findSpace curr cnt = -- part of target slot, # of bytes left to check
280 if Map.member curr conflicts then
281 findSpace (align (curr + size)) size -- try the next (possibly) open space
282 else findSpace (curr - 1) (cnt - 1)
283 in findSpace (align (offset + size)) size
285 -- Find an open space on the stack, and assign it to the area.
286 allocSlotFrom :: Ord x => IGPair x -> AreaMap -> Int -> AreaMap -> Area -> AreaMap
287 allocSlotFrom ig areaSize from areaMap area =
288 if Map.member area areaMap then areaMap
289 else Map.insert area (freeSlotFrom ig areaSize from areaMap area) areaMap
291 -- | Greedy stack layout.
292 -- Compute liveness, build the interference graph, and allocate slots for the areas.
293 -- We visit each basic block in a (generally) forward order.
295 -- At each instruction that names a register subarea r, we immediately allocate
296 -- any available slot on the stack by the following procedure:
297 -- 1. Find the sub-areas S that conflict with r
298 -- 2. Find the stack slots used for S
299 -- 3. Choose a contiguous stack space s not in S (s must be large enough to hold r)
301 -- For a CallArea, we allocate the stack space only when we reach a function
302 -- call that returns to the CallArea's blockId.
303 -- Then, we allocate the Area subject to the following constraints:
304 -- a) It must be younger than all the sub-areas that are live on entry to the block
305 -- This constraint is only necessary for the successor of a call
306 -- b) It must not overlap with any already-allocated Area with which it conflicts
307 -- (ie at some point, not necessarily now, is live at the same time)
308 -- Part (b) is just the 1,2,3 part above
310 -- Note: The stack pointer only has to be younger than the youngest live stack slot
311 -- at proc points. Otherwise, the stack pointer can point anywhere.
313 layout :: ProcPointSet -> SlotEnv -> ByteOff -> LGraph Middle Last -> AreaMap
314 -- The domain of the returned map includes an Area for EVERY block
315 -- including each block that is not the successor of a call (ie is not a proc-point)
316 -- That's how we return the info of what the SP should be at the entry of every block
318 layout procPoints env entry_off g =
319 let ig = (igraph areaBuilder env g, areaBuilder)
320 env' bid = lookupBlockEnv env bid `orElse` panic "unknown blockId in igraph"
321 areaSize = getAreaSize entry_off g
322 -- Find the slots that are live-in to a block tail
323 live_in (ZTail m l) = liveInSlots m (live_in l)
324 live_in (ZLast (LastOther l)) = liveLastIn l env'
325 live_in (ZLast LastExit) = Map.empty
327 -- Find the youngest live stack slot that has already been allocated
328 youngest_live :: AreaMap -- Already allocated
329 -> SubAreaSet -- Sub-areas live here
330 -> ByteOff -- Offset of the youngest byte of any
331 -- already-allocated, live sub-area
332 youngest_live areaMap live = fold_subareas young_slot live 0
333 where young_slot (a, o, _) z = case Map.lookup a areaMap of
334 Just top -> max z $ top + o
336 fold_subareas f m z = Map.foldRightWithKey (\_ s z -> foldr f z s) z m
338 -- Allocate space for spill slots and call areas
339 allocVarSlot = allocSlotFrom ig areaSize 0
341 -- Update the successor's incoming SP.
342 setSuccSPs inSp bid areaMap =
343 case (Map.lookup area areaMap, lookupBlockEnv (lg_blocks g) bid) of
344 (Just _, _) -> areaMap -- succ already knows incoming SP
345 (Nothing, Just (Block _ _)) ->
346 if elemBlockSet bid procPoints then
347 let young = youngest_live areaMap $ env' bid
348 -- start = case returnOff stackInfo of Just b -> max b young
350 start = young -- maybe wrong, but I don't understand
351 -- why the preceding is necessary...
352 in allocSlotFrom ig areaSize start areaMap area
353 else Map.insert area inSp areaMap
354 (_, Nothing) -> panic "Block not found in cfg"
355 where area = CallArea (Young bid)
357 allocLast (Block id _) areaMap l =
358 fold_succs (setSuccSPs inSp) l areaMap
359 where inSp = expectJust "sp in" $ Map.lookup (CallArea (Young id)) areaMap
361 allocMidCall m@(MidForeignCall (Safe bid _) _ _ _) t areaMap =
362 let young = youngest_live areaMap $ removeLiveSlotDefs (live_in t) m
363 area = CallArea (Young bid)
364 areaSize' = Map.insert area (widthInBytes (typeWidth gcWord)) areaSize
365 in allocSlotFrom ig areaSize' young areaMap area
366 allocMidCall _ _ areaMap = areaMap
369 foldSlotsDefd alloc' (foldSlotsUsed alloc' (allocMidCall m t areaMap) m) m
370 where alloc' areaMap (a@(RegSlot _), _, _) = allocVarSlot areaMap a
371 alloc' areaMap _ = areaMap
373 layoutAreas areaMap b@(Block _ t) = layout areaMap t
374 where layout areaMap (ZTail m t) = layout (alloc m t areaMap) t
375 layout areaMap (ZLast l) = allocLast b areaMap l
376 initMap = Map.insert (CallArea (Young (lg_entry g))) 0
377 (Map.insert (CallArea Old) 0 Map.empty)
378 areaMap = foldl layoutAreas initMap (postorder_dfs g)
379 in -- pprTrace "ProcPoints" (ppr procPoints) $
380 -- pprTrace "Area SizeMap" (ppr areaSize) $
381 -- pprTrace "Entry SP" (ppr entrySp) $
382 -- pprTrace "Area Map" (ppr areaMap) $
385 -- After determining the stack layout, we can:
386 -- 1. Replace references to stack Areas with addresses relative to the stack
388 -- 2. Insert adjustments to the stack pointer to ensure that it is at a
389 -- conventional location at each proc point.
390 -- Because we don't take interrupts on the execution stack, we only need the
391 -- stack pointer to be younger than the live values on the stack at proc points.
392 -- 3. Compute the maximum stack offset used in the procedure and replace
393 -- the stack high-water mark with that offset.
394 manifestSP :: AreaMap -> ByteOff -> LGraph Middle Last -> FuelMonad (LGraph Middle Last)
395 manifestSP areaMap entry_off g@(LGraph entry _blocks) =
396 liftM (LGraph entry) $ foldl replB (return emptyBlockEnv) (postorder_dfs g)
397 where slot a = -- pprTrace "slot" (ppr a) $
398 Map.lookup a areaMap `orElse` panic "unallocated Area"
399 slot' (Just id) = slot $ CallArea (Young id)
400 slot' Nothing = slot $ CallArea Old
401 sp_high = maxSlot slot g
402 proc_entry_sp = slot (CallArea Old) + entry_off
405 case Z.last (unzip b) of
406 LastOther (LastCall {cml_cont = Just succ, cml_ret_args = off}) ->
407 extendBlockEnv env succ off
409 spEntryMap = fold_blocks add_sp_off (mkBlockEnv [(entry, entry_off)]) g
410 spOffset id = lookupBlockEnv spEntryMap id `orElse` 0
412 sp_on_entry id | id == entry = proc_entry_sp
413 sp_on_entry id = slot' (Just id) + spOffset id
415 -- On entry to procpoints, the stack pointer is conventional;
416 -- otherwise, we check the SP set by predecessors.
417 replB :: FuelMonad (BlockEnv CmmBlock) -> CmmBlock -> FuelMonad (BlockEnv CmmBlock)
418 replB blocks (Block id t) =
419 do bs <- replTail (Block id) spIn t
420 -- pprTrace "spIn" (ppr id <+> ppr spIn) $ do
421 liftM (flip (foldr insertBlock) bs) blocks
422 where spIn = sp_on_entry id
423 replTail :: (ZTail Middle Last -> CmmBlock) -> Int -> (ZTail Middle Last) ->
424 FuelMonad ([CmmBlock])
425 replTail h spOff (ZTail m@(MidForeignCall (Safe bid _) _ _ _) t) =
426 replTail (\t' -> h (setSp spOff spOff' (ZTail (middle spOff m) t'))) spOff' t
427 where spOff' = slot' (Just bid) + widthInBytes (typeWidth gcWord)
428 replTail h spOff (ZTail m t) = replTail (h . ZTail (middle spOff m)) spOff t
429 replTail h spOff (ZLast (LastOther l)) = fixSp h spOff l
430 replTail h _ l@(ZLast LastExit) = return [h l]
431 middle spOff m = mapExpDeepMiddle (replSlot spOff) m
432 last spOff l = mapExpDeepLast (replSlot spOff) l
433 replSlot spOff (CmmStackSlot a i) = CmmRegOff (CmmGlobal Sp) (spOff - (slot a + i))
434 replSlot _ (CmmLit CmmHighStackMark) = -- replacing the high water mark
435 CmmLit (CmmInt (toInteger (max 0 (sp_high - proc_entry_sp))) (typeWidth bWord))
437 -- The block must establish the SP expected at each successsor.
438 fixSp :: (ZTail Middle Last -> CmmBlock) -> Int -> Last -> FuelMonad ([CmmBlock])
439 fixSp h spOff l@(LastCall _ k n _ _) = updSp h spOff (slot' k + n) l
440 fixSp h spOff l@(LastBranch k) =
441 let succSp = sp_on_entry k in
442 if succSp /= spOff then
443 -- pprTrace "updSp" (ppr k <> ppr spOff <> ppr (sp_on_entry k)) $
444 updSp h spOff succSp l
445 else return $ [h (ZLast (LastOther (last spOff l)))]
446 fixSp h spOff l = liftM (uncurry (:)) $ fold_succs succ l $ return (b, [])
447 where b = h (ZLast (LastOther (last spOff l)))
449 let succSp = sp_on_entry succId in
450 if succSp /= spOff then
452 (b', bs') <- insertBetween b [setSpMid spOff succSp] succId
453 return (b', bs ++ bs')
455 updSp h old new l = return [h $ setSp old new $ ZLast $ LastOther (last new l)]
456 setSpMid sp sp' = MidAssign (CmmGlobal Sp) e
457 where e = CmmMachOp (MO_Add wordWidth) [CmmReg (CmmGlobal Sp), off]
458 off = CmmLit $ CmmInt (toInteger $ sp - sp') wordWidth
459 setSp sp sp' t = if sp == sp' then t else ZTail (setSpMid sp sp') t
462 -- To compute the stack high-water mark, we fold over the graph and
463 -- compute the highest slot offset.
464 maxSlot :: (Area -> Int) -> CmmGraph -> Int
465 maxSlot slotOff g = fold_blocks (fold_fwd_block (\ _ x -> x) highSlot highSlot) 0 g
466 where highSlot i z = foldSlotsUsed add (foldSlotsDefd add z i) i
467 add z (a, i, _) = max z (slotOff a + i)
469 -----------------------------------------------------------------------------
470 -- | Sanity check: stub pointers immediately after they die
471 -----------------------------------------------------------------------------
472 -- This will miss stack slots that are last used in a Last node,
473 -- but it should do pretty well...
475 type StubPtrFix = FuelMonad (BackwardFixedPoint Middle Last SubAreaSet CmmGraph)
477 stubSlotsOnDeath :: (LGraph Middle Last) -> FuelMonad (LGraph Middle Last)
478 stubSlotsOnDeath g = liftM zdfFpContents $ (res :: StubPtrFix)
479 where res = zdfBRewriteFromL RewriteShallow emptyBlockEnv "stub ptrs" slotLattice
480 liveSlotTransfers rewrites (fact_bot slotLattice) g
481 rewrites = BackwardRewrites first middle last Nothing
484 middle m liveSlots = foldSlotsUsed (stub liveSlots m) Nothing m
485 stub liveSlots m rst subarea@(a, off, w) =
486 if elemSlot liveSlots subarea then rst
487 else let store = mkStore (CmmStackSlot a off)
488 (stackStubExpr (widthFromBytes w))
489 in case rst of Nothing -> Just (mkMiddle m <*> store)
490 Just g -> Just (g <*> store)