add -fsimpleopt-before-flatten
[ghc-hetmet.git] / compiler / cmm / CmmStackLayout.hs
1 {-# OPTIONS_GHC -XGADTs -XNoMonoLocalBinds #-}
2 -- Norman likes local bindings
3 -- If this module lives on I'd like to get rid of this flag in due course
4
5 -- Todo: remove
6 {-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
7
8 {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}
9 #if __GLASGOW_HASKELL__ >= 701
10 -- GHC 7.0.1 improved incomplete pattern warnings with GADTs
11 {-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
12 #endif
13
14 module CmmStackLayout
15     ( SlotEnv, liveSlotAnal, liveSlotTransfers, removeLiveSlotDefs
16     , layout, manifestSP, igraph, areaBuilder
17     , stubSlotsOnDeath ) -- to help crash early during debugging
18 where
19
20 import Constants
21 import Prelude hiding (succ, zip, unzip, last)
22
23 import BlockId
24 import Cmm
25 import CmmExpr
26 import CmmProcPoint
27 import Maybes
28 import MkGraph (stackStubExpr)
29 import Control.Monad
30 import OptimizationFuel
31 import Outputable
32 import SMRep (ByteOff)
33
34 import Compiler.Hoopl
35
36 import Data.Map (Map)
37 import qualified Data.Map as Map
38 import qualified FiniteMap as Map
39
40 ------------------------------------------------------------------------
41 --                    Stack Layout                                    --
42 ------------------------------------------------------------------------
43
44 -- | Before we lay out the stack, we need to know something about the
45 -- liveness of the stack slots. In particular, to decide whether we can
46 -- reuse a stack location to hold multiple stack slots, we need to know
47 -- when each of the stack slots is used.
48 -- Although tempted to use something simpler, we really need a full interference
49 -- graph. Consider the following case:
50 --   case <...> of
51 --     1 -> <spill x>; // y is dead out
52 --     2 -> <spill y>; // x is dead out
53 --     3 -> <spill x and y>
54 -- If we consider the arms in order and we use just the deadness information given by a
55 -- dataflow analysis, we might decide to allocate the stack slots for x and y
56 -- to the same stack location, which will lead to incorrect code in the third arm.
57 -- We won't make this mistake with an interference graph.
58
59 -- First, the liveness analysis.
60 -- We represent a slot with an area, an offset into the area, and a width.
61 -- Tracking the live slots is a bit tricky because there may be loads and stores
62 -- into only a part of a stack slot (e.g. loading the low word of a 2-word long),
63 -- e.g. Slot A 0 8 overlaps with Slot A 4 4.
64 --
65 -- The definition of a slot set is intended to reduce the number of overlap
66 -- checks we have to make. There's no reason to check for overlap between
67 -- slots in different areas, so we segregate the map by Area's.
68 -- We expect few slots in each Area, so we collect them in an unordered list.
69 -- To keep these lists short, any contiguous live slots are coalesced into
70 -- a single slot, on insertion.
71
72 slotLattice :: DataflowLattice SubAreaSet
73 slotLattice = DataflowLattice "live slots" Map.empty add
74   where add _ (OldFact old) (NewFact new) = case Map.foldRightWithKey addArea (False, old) new of
75                                               (change, x) -> (changeIf change, x)
76         addArea a newSlots z = foldr (addSlot a) z newSlots
77         addSlot a slot (changed, map) =
78           let (c, live) = liveGen slot $ Map.findWithDefault [] a map
79           in (c || changed, Map.insert a live map)
80
81 slotLatticeJoin :: [SubAreaSet] -> SubAreaSet
82 slotLatticeJoin facts = foldr extend (fact_bot slotLattice) facts
83   where extend fact res = snd $ fact_join slotLattice undefined (OldFact fact) (NewFact res)
84
85 type SlotEnv   = BlockEnv SubAreaSet
86   -- The sub-areas live on entry to the block
87
88 liveSlotAnal :: CmmGraph -> FuelUniqSM SlotEnv
89 liveSlotAnal g = liftM snd $ dataflowPassBwd g [] $ analBwd slotLattice liveSlotTransfers
90
91 -- Add the subarea s to the subareas in the list-set (possibly coalescing it with
92 -- adjacent subareas), and also return whether s was a new addition.
93 liveGen :: SubArea -> [SubArea] -> (Bool, [SubArea])
94 liveGen s set = liveGen' s set []
95   where liveGen' s [] z = (True, s : z)
96         liveGen' s@(a, hi, w) (s'@(a', hi', w') : rst) z =
97           if a /= a' || hi < lo' || lo > hi' then    -- no overlap
98             liveGen' s rst (s' : z)
99           else if s' `contains` s then               -- old contains new
100             (False, set)
101           else                                       -- overlap: coalesce the slots
102             let new_hi = max hi hi'
103                 new_lo = min lo lo'
104             in liveGen' (a, new_hi, new_hi - new_lo) rst z
105           where lo  = hi  - w  -- remember: areas grow down
106                 lo' = hi' - w'
107         contains (a, hi, w) (a', hi', w') =
108           a == a' && hi >= hi' && hi - w <= hi' - w'
109
110 liveKill :: SubArea -> [SubArea] -> [SubArea]
111 liveKill (a, hi, w) set = -- pprTrace "killing slots in area" (ppr a) $
112                           liveKill' set []
113   where liveKill' [] z = z
114         liveKill' (s'@(a', hi', w') : rst) z =
115           if a /= a' || hi < lo' || lo > hi' then    -- no overlap
116             liveKill' rst (s' : z)
117           else                                       -- overlap: split the old slot
118             let z'  = if hi' > hi  then (a, hi', hi' - hi)  : z else z
119                 z'' = if lo  > lo' then (a, lo,  lo  - lo') : z' else z'
120             in liveKill' rst z''
121           where lo  = hi  - w  -- remember: areas grow down
122                 lo' = hi' - w'
123
124 -- Note: the stack slots that hold variables returned on the stack are not
125 -- considered live in to the block -- we treat the first node as a definition site.
126 -- BEWARE?: Am I being a little careless here in failing to check for the
127 -- entry Id (which would use the CallArea Old).
128 liveSlotTransfers :: BwdTransfer CmmNode SubAreaSet
129 liveSlotTransfers = mkBTransfer3 frt mid lst
130   where frt :: CmmNode C O -> SubAreaSet -> SubAreaSet
131         frt (CmmEntry l) f = Map.delete (CallArea (Young l)) f
132
133         mid :: CmmNode O O -> SubAreaSet -> SubAreaSet
134         mid n f = foldSlotsUsed addSlot (removeLiveSlotDefs f n) n
135         lst :: CmmNode O C -> FactBase SubAreaSet -> SubAreaSet
136         lst n f = liveInSlots n $ case n of
137           CmmCall {cml_cont=Nothing, cml_args=args} -> add_area (CallArea Old) args out
138           CmmCall {cml_cont=Just k, cml_args=args}  -> add_area (CallArea Old) args (add_area (CallArea (Young k)) args out)
139           CmmForeignCall {succ=k, updfr=oldend}     -> add_area (CallArea Old) oldend (add_area (CallArea (Young k)) wORD_SIZE out)
140           _                                         -> out
141          where out = joinOutFacts slotLattice n f
142                add_area _ n live | n == 0 = live
143                add_area a n live = Map.insert a (snd $ liveGen (a, n, n) $ Map.findWithDefault [] a live) live
144
145 -- Slot sets: adding slots, removing slots, and checking for membership.
146 liftToArea :: Area -> ([SubArea] -> [SubArea]) -> SubAreaSet -> SubAreaSet 
147 addSlot, removeSlot :: SubAreaSet -> SubArea -> SubAreaSet
148 elemSlot            :: SubAreaSet -> SubArea -> Bool
149 liftToArea a f map = Map.insert a (f (Map.findWithDefault [] a map)) map
150 addSlot    live (a, i, w) = liftToArea a (snd . liveGen  (a, i, w)) live
151 removeSlot live (a, i, w) = liftToArea a       (liveKill (a, i, w)) live
152 elemSlot   live (a, i, w) =
153   not $ fst $ liveGen  (a, i, w) (Map.findWithDefault [] a live)
154
155 removeLiveSlotDefs :: (DefinerOfSlots s, UserOfSlots s) => SubAreaSet -> s -> SubAreaSet
156 removeLiveSlotDefs = foldSlotsDefd removeSlot
157
158 liveInSlots :: (DefinerOfSlots s, UserOfSlots s) => s -> SubAreaSet -> SubAreaSet
159 liveInSlots x live = foldSlotsUsed addSlot (removeLiveSlotDefs live x) x
160
161 liveLastIn :: CmmNode O C -> (BlockId -> SubAreaSet) -> SubAreaSet
162 liveLastIn l env = liveInSlots l (liveLastOut env l)
163
164 -- Don't forget to keep the outgoing parameters in the CallArea live,
165 -- as well as the update frame.
166 -- Note: We have to keep the update frame live at a call because of the
167 -- case where the function doesn't return -- in that case, there won't
168 -- be a return to keep the update frame live. We'd still better keep the
169 -- info pointer in the update frame live at any call site;
170 -- otherwise we could screw up the garbage collector.
171 liveLastOut :: (BlockId -> SubAreaSet) -> CmmNode O C -> SubAreaSet
172 liveLastOut env l =
173   case l of
174     CmmCall _ Nothing n _ _ -> 
175       add_area (CallArea Old) n out -- add outgoing args (includes upd frame)
176     CmmCall _ (Just k) n _ _ ->
177       add_area (CallArea Old) n (add_area (CallArea (Young k)) n out)
178     CmmForeignCall { succ = k, updfr = oldend } ->
179       add_area (CallArea Old) oldend (add_area (CallArea (Young k)) wORD_SIZE out)
180     _ -> out
181   where out = slotLatticeJoin $ map env $ successors l
182         add_area _ n live | n == 0 = live
183         add_area a n live =
184           Map.insert a (snd $ liveGen (a, n, n) $ Map.findWithDefault [] a live) live
185
186 -- The liveness analysis must be precise: otherwise, we won't know if a definition
187 -- should really kill a live-out stack slot.
188 -- But the interference graph does not have to be precise -- it might decide that
189 -- any live areas interfere. To maintain both a precise analysis and an imprecise
190 -- interference graph, we need to convert the live-out stack slots to graph nodes
191 -- at each and every instruction; rather than reconstruct a new list of nodes
192 -- every time, I provide a function to fold over the nodes, which should be a
193 -- reasonably efficient approach for the implementations we envision.
194 -- Of course, it will probably be much easier to program if we just return a list...
195 type Set x = Map x ()
196 data IGraphBuilder n =
197   Builder { foldNodes     :: forall z. SubArea -> (n -> z -> z) -> z -> z
198           , _wordsOccupied :: AreaMap -> AreaMap -> n -> [Int]
199           }
200
201 areaBuilder :: IGraphBuilder Area
202 areaBuilder = Builder fold words
203   where fold (a, _, _) f z = f a z
204         words areaSize areaMap a =
205           case Map.lookup a areaMap of
206             Just addr -> [addr .. addr + (Map.lookup a areaSize `orElse`
207                                           pprPanic "wordsOccupied: unknown area" (ppr areaSize <+> ppr a))]
208             Nothing   -> []
209
210 --slotBuilder :: IGraphBuilder (Area, Int)
211 --slotBuilder = undefined
212
213 -- Now, we can build the interference graph.
214 -- The usual story: a definition interferes with all live outs and all other
215 -- definitions.
216 type IGraph x = Map x (Set x)
217 type IGPair x = (IGraph x, IGraphBuilder x)
218 igraph :: (Ord x) => IGraphBuilder x -> SlotEnv -> CmmGraph -> IGraph x
219 igraph builder env g = foldr interfere Map.empty (postorderDfs g)
220   where foldN = foldNodes builder
221         interfere block igraph = foldBlockNodesB3 (first, middle, last) block igraph
222           where first _ (igraph, _) = igraph
223                 middle node (igraph, liveOut) =
224                   (addEdges igraph node liveOut, liveInSlots node liveOut)
225                 last node igraph =
226                   (addEdges igraph node $ liveLastOut env' node, liveLastIn node env')
227
228                 -- add edges between a def and the other defs and liveouts
229                 addEdges igraph i out = fst $ foldSlotsDefd addDef (igraph, out) i
230                 addDef (igraph, out) def@(a, _, _) =
231                   (foldN def (addDefN out) igraph,
232                    Map.insert a (snd $ liveGen def (Map.findWithDefault [] a out)) out)
233                 addDefN out n igraph =
234                   let addEdgeNO o igraph = foldN o addEdgeNN igraph
235                       addEdgeNN n' igraph = addEdgeNN' n n' $ addEdgeNN' n' n igraph
236                       addEdgeNN' n n' igraph = Map.insert n (Map.insert n' () set) igraph
237                         where set = Map.findWithDefault Map.empty n igraph
238                   in Map.foldRightWithKey (\ _ os igraph -> foldr addEdgeNO igraph os) igraph out
239                 env' bid = mapLookup bid env `orElse` panic "unknown blockId in igraph"
240
241 -- Before allocating stack slots, we need to collect one more piece of information:
242 -- what's the highest offset (in bytes) used in each Area?
243 -- We'll need to allocate that much space for each Area.
244
245 -- JD: WHY CAN'T THIS COME FROM THE slot-liveness info?
246 getAreaSize :: ByteOff -> CmmGraph -> AreaMap
247   -- The domain of the returned mapping consists only of Areas
248   -- used for (a) variable spill slots, and (b) parameter passing ares for calls
249 getAreaSize entry_off g =
250   foldGraphBlocks (foldBlockNodesF3 (first, add_regslots, last))
251               (Map.singleton (CallArea Old) entry_off) g
252   where first _  z = z
253         last :: CmmNode O C -> Map Area Int -> Map Area Int
254         last l@(CmmCall _ Nothing args res _) z  =  add_regslots l (add (add z area args) area res)
255           where area = CallArea Old
256         last l@(CmmCall _ (Just k) args res _) z =  add_regslots l (add (add z area args) area res)
257           where area = CallArea (Young k)
258         last l@(CmmForeignCall {succ = k}) z     =  add_regslots l (add z area wORD_SIZE)
259           where area = CallArea (Young k)
260         last l z                                 =  add_regslots l z
261         add_regslots i z = foldSlotsUsed addSlot (foldSlotsDefd addSlot z i) i
262         addSlot z (a@(RegSlot (LocalReg _ ty)), _, _) =
263           add z a $ widthInBytes $ typeWidth ty
264         addSlot z _ = z
265         add z a off = Map.insert a (max off (Map.findWithDefault 0 a z)) z
266         -- The 'max' is important.  Two calls, to f and g, might share a common
267         -- continuation (and hence a common CallArea), but their number of overflow
268         -- parameters might differ.
269
270
271 -- Find the Stack slots occupied by the subarea's conflicts
272 conflictSlots :: Ord x => IGPair x -> AreaMap -> AreaMap -> SubArea -> Set Int
273 conflictSlots (ig, Builder foldNodes wordsOccupied) areaSize areaMap subarea =
274   foldNodes subarea foldNode Map.empty
275   where foldNode n set = Map.foldRightWithKey conflict set $ Map.findWithDefault Map.empty n ig
276         conflict n' () set = liveInSlots areaMap n' set
277         -- Add stack slots occupied by igraph node n
278         liveInSlots areaMap n set = foldr setAdd set (wordsOccupied areaSize areaMap n)
279         setAdd w s = Map.insert w () s
280
281 -- Find any open space on the stack, starting from the offset.
282 -- If the area is a CallArea or a spill slot for a pointer, then it must
283 -- be word-aligned.
284 freeSlotFrom :: Ord x => IGPair x -> AreaMap -> Int -> AreaMap -> Area -> Int
285 freeSlotFrom ig areaSize offset areaMap area =
286   let size = Map.lookup area areaSize `orElse` 0
287       conflicts = conflictSlots ig areaSize areaMap (area, size, size)
288       -- CallAreas and Ptrs need to be word-aligned (round up!)
289       align = case area of CallArea _                                -> align'
290                            RegSlot  r | isGcPtrType (localRegType r) -> align'
291                            RegSlot  _                                -> id
292       align' n = (n + (wORD_SIZE - 1)) `div` wORD_SIZE * wORD_SIZE
293       -- Find a space big enough to hold the area
294       findSpace curr 0 = curr
295       findSpace curr cnt = -- part of target slot, # of bytes left to check
296         if Map.member curr conflicts then
297           findSpace (align (curr + size)) size -- try the next (possibly) open space
298         else findSpace (curr - 1) (cnt - 1)
299   in findSpace (align (offset + size)) size
300
301 -- Find an open space on the stack, and assign it to the area.
302 allocSlotFrom :: Ord x => IGPair x -> AreaMap -> Int -> AreaMap -> Area -> AreaMap
303 allocSlotFrom ig areaSize from areaMap area =
304   if Map.member area areaMap then areaMap
305   else Map.insert area (freeSlotFrom ig areaSize from areaMap area) areaMap
306
307 -- | Greedy stack layout.
308 -- Compute liveness, build the interference graph, and allocate slots for the areas.
309 -- We visit each basic block in a (generally) forward order.
310
311 -- At each instruction that names a register subarea r, we immediately allocate
312 -- any available slot on the stack by the following procedure:
313 --  1. Find the sub-areas S that conflict with r
314 --  2. Find the stack slots used for S
315 --  3. Choose a contiguous stack space s not in S (s must be large enough to hold r)
316
317 -- For a CallArea, we allocate the stack space only when we reach a function
318 -- call that returns to the CallArea's blockId.
319 -- Then, we allocate the Area subject to the following constraints:
320 --   a) It must be younger than all the sub-areas that are live on entry to the block
321 --         This constraint is only necessary for the successor of a call
322 --   b) It must not overlap with any already-allocated Area with which it conflicts
323 --         (ie at some point, not necessarily now, is live at the same time)
324 --   Part (b) is just the 1,2,3 part above
325
326 -- Note: The stack pointer only has to be younger than the youngest live stack slot
327 -- at proc points. Otherwise, the stack pointer can point anywhere.
328
329 layout :: ProcPointSet -> SlotEnv -> ByteOff -> CmmGraph -> AreaMap
330 -- The domain of the returned map includes an Area for EVERY block
331 -- including each block that is not the successor of a call (ie is not a proc-point)
332 -- That's how we return the info of what the SP should be at the entry of every block
333
334 layout procPoints env entry_off g =
335   let ig = (igraph areaBuilder env g, areaBuilder)
336       env' bid = mapLookup bid env `orElse` panic "unknown blockId in igraph"
337       areaSize = getAreaSize entry_off g
338
339       -- Find the youngest live stack slot that has already been allocated
340       youngest_live :: AreaMap     -- Already allocated
341                     -> SubAreaSet  -- Sub-areas live here
342                     -> ByteOff     -- Offset of the youngest byte of any 
343                                    --    already-allocated, live sub-area
344       youngest_live areaMap live = fold_subareas young_slot live 0
345         where young_slot (a, o, _) z = case Map.lookup a areaMap of
346                                          Just top -> max z $ top + o
347                                          Nothing  -> z
348               fold_subareas f m z = Map.foldRightWithKey (\_ s z -> foldr f z s) z m
349
350       -- Allocate space for spill slots and call areas
351       allocVarSlot = allocSlotFrom ig areaSize 0
352
353       -- Update the successor's incoming SP.
354       setSuccSPs inSp bid areaMap =
355         case (Map.lookup area areaMap , mapLookup bid (toBlockMap g)) of
356           (Just _, _) -> areaMap -- succ already knows incoming SP
357           (Nothing, Just _) ->
358             if setMember bid procPoints then
359               let young = youngest_live areaMap $ env' bid
360                   -- start = case returnOff stackInfo of Just b  -> max b young
361                   --                                     Nothing -> young
362                   start = young -- maybe wrong, but I don't understand
363                                 -- why the preceding is necessary...
364               in  allocSlotFrom ig areaSize start areaMap area
365             else Map.insert area inSp areaMap
366           (_, Nothing) -> panic "Block not found in cfg"
367         where area = CallArea (Young bid)
368
369       layoutAreas areaMap block = foldBlockNodesF3 (flip const, allocMid, allocLast (entryLabel block)) block areaMap
370       allocMid m areaMap = foldSlotsDefd alloc' (foldSlotsUsed alloc' areaMap m) m
371       allocLast bid l areaMap =
372         foldr (setSuccSPs inSp) areaMap' (successors l)
373         where inSp = expectJust "sp in" $ Map.lookup (CallArea (Young bid)) areaMap
374               areaMap' = foldSlotsDefd alloc' (foldSlotsUsed alloc' areaMap l) l
375       alloc' areaMap (a@(RegSlot _), _, _) = allocVarSlot areaMap a
376       alloc' areaMap _ = areaMap
377
378       initMap = Map.insert (CallArea (Young (g_entry g))) 0 $
379                   Map.insert (CallArea Old) 0 Map.empty
380                         
381       areaMap = foldl layoutAreas initMap (postorderDfs g)
382   in -- pprTrace "ProcPoints" (ppr procPoints) $
383         -- pprTrace "Area SizeMap" (ppr areaSize) $
384          -- pprTrace "Entry SP" (ppr entrySp) $
385            -- pprTrace "Area Map" (ppr areaMap) $
386      areaMap
387
388 -- After determining the stack layout, we can:
389 -- 1. Replace references to stack Areas with addresses relative to the stack
390 --    pointer.
391 -- 2. Insert adjustments to the stack pointer to ensure that it is at a
392 --    conventional location at each proc point.
393 --    Because we don't take interrupts on the execution stack, we only need the
394 --    stack pointer to be younger than the live values on the stack at proc points.
395 -- 3. Compute the maximum stack offset used in the procedure and replace
396 --    the stack high-water mark with that offset.
397 manifestSP :: AreaMap -> ByteOff -> CmmGraph -> FuelUniqSM CmmGraph
398 manifestSP areaMap entry_off g@(CmmGraph {g_entry=entry}) =
399   ofBlockMap entry `liftM` foldl replB (return mapEmpty) (postorderDfs g)
400   where slot a = -- pprTrace "slot" (ppr a) $
401                    Map.lookup a areaMap `orElse` panic "unallocated Area"
402         slot' (Just id) = slot $ CallArea (Young id)
403         slot' Nothing   = slot $ CallArea Old
404         sp_high = maxSlot slot g
405         proc_entry_sp = slot (CallArea Old) + entry_off
406
407         add_sp_off :: CmmBlock -> BlockEnv Int -> BlockEnv Int
408         add_sp_off b env =
409           case lastNode b of
410             CmmCall {cml_cont=Just succ, cml_ret_args=off} -> mapInsert succ off env
411             CmmForeignCall {succ=succ}                     -> mapInsert succ wORD_SIZE env
412             _                                              -> env
413         spEntryMap = foldGraphBlocks add_sp_off (mapInsert entry entry_off emptyBlockMap) g
414         spOffset id = mapLookup id spEntryMap `orElse` 0
415
416         sp_on_entry id | id == entry = proc_entry_sp
417         sp_on_entry id = slot' (Just id) + spOffset id
418
419         -- On entry to procpoints, the stack pointer is conventional;
420         -- otherwise, we check the SP set by predecessors.
421         replB :: FuelUniqSM (BlockEnv CmmBlock) -> CmmBlock -> FuelUniqSM (BlockEnv CmmBlock)
422         replB blocks block =
423           do let (head, middles, JustC tail :: MaybeC C (CmmNode O C)) = blockToNodeList block
424                  middles' = map (middle spIn) middles
425              bs <- replLast head middles' tail
426              flip (foldr insertBlock) bs `liftM` blocks
427           where spIn = sp_on_entry (entryLabel block)
428
429                 middle spOff m = mapExpDeep (replSlot spOff) m
430                 last   spOff l = mapExpDeep (replSlot spOff) l
431                 replSlot spOff (CmmStackSlot a i) = CmmRegOff (CmmGlobal Sp) (spOff - (slot a + i))
432                 replSlot _ (CmmLit CmmHighStackMark) = -- replacing the high water mark
433                   CmmLit (CmmInt (toInteger (max 0 (sp_high - proc_entry_sp))) (typeWidth bWord))
434                 replSlot _ e = e
435
436                 replLast :: MaybeC C (CmmNode C O) -> [CmmNode O O] -> CmmNode O C -> FuelUniqSM [CmmBlock]
437                 replLast h m l@(CmmCall _ k n _ _)       = updSp (slot' k + n) h m l
438                 -- JD: LastForeignCall probably ought to have an outgoing
439                 --     arg size, just like LastCall
440                 replLast h m l@(CmmForeignCall {succ=k}) = updSp (slot' (Just k) + wORD_SIZE) h m l
441                 replLast h m l@(CmmBranch k)             = updSp (sp_on_entry k) h m l
442                 replLast h m l                           = uncurry (:) `liftM` foldr succ (return (b, [])) (successors l)
443                   where b :: CmmBlock
444                         b = updSp' spIn h m l
445                         succ succId z =
446                           let succSp = sp_on_entry succId in
447                           if succSp /= spIn then
448                             do (b,  bs)  <- z
449                                (b', bs') <- insertBetween b (adjustSp succSp) succId
450                                return (b', bs' ++ bs)
451                           else z
452
453                 updSp sp h m l = return [updSp' sp h m l]
454                 updSp' sp h m l | sp == spIn = blockOfNodeList (h, m, JustC $ last sp l)
455                                 | otherwise  = blockOfNodeList (h, m ++ adjustSp sp, JustC $ last sp l)
456                 adjustSp sp = [CmmAssign (CmmGlobal Sp) e]
457                   where e = CmmMachOp (MO_Add wordWidth) [CmmReg (CmmGlobal Sp), off]
458                         off = CmmLit $ CmmInt (toInteger $ spIn - sp) wordWidth
459
460
461 -- To compute the stack high-water mark, we fold over the graph and
462 -- compute the highest slot offset.
463 maxSlot :: (Area -> Int) -> CmmGraph -> Int
464 maxSlot slotOff g = foldGraphBlocks (foldBlockNodesF3 (flip const, highSlot, highSlot)) 0 g
465   where highSlot i z = foldSlotsUsed add (foldSlotsDefd add z i) i
466         add z (a, i, _) = max z (slotOff a + i)
467
468 -----------------------------------------------------------------------------
469 -- | Sanity check: stub pointers immediately after they die
470 -----------------------------------------------------------------------------
471 -- This will miss stack slots that are last used in a Last node,
472 -- but it should do pretty well...
473
474 stubSlotsOnDeath :: CmmGraph -> FuelUniqSM CmmGraph
475 stubSlotsOnDeath g = liftM fst $ dataflowPassBwd g [] $ analRewBwd slotLattice
476                                                                    liveSlotTransfers
477                                                                    rewrites
478     where rewrites = mkBRewrite3 frt mid lst
479           frt _ _ = return Nothing
480           mid m liveSlots = return $ foldSlotsUsed (stub liveSlots m) Nothing m
481           lst _ _ = return Nothing
482           stub liveSlots m rst subarea@(a, off, w) =
483             if elemSlot liveSlots subarea then rst
484             else let store = mkMiddle $ CmmStore (CmmStackSlot a off)
485                                                  (stackStubExpr (widthFromBytes w))
486                  in case rst of Nothing -> Just (mkMiddle m <*> store)
487                                 Just g  -> Just (g <*> store)