Removed warnings, made Haddock happy, added examples in documentation
[ghc-hetmet.git] / compiler / cmm / CmmBuildInfoTables.hs
1 module CmmBuildInfoTables
2     ( CAFSet, CAFEnv, CmmTopForInfoTables(..), cafAnal, localCAFInfo, mkTopCAFInfo
3     , setInfoTableSRT, setInfoTableStackMap
4     , TopSRT, emptySRT, srtToData
5     , bundleCAFs
6     , finishInfoTables, lowerSafeForeignCalls, extendEnvsForSafeForeignCalls )
7 where
8
9 #include "HsVersions.h"
10
11 import Constants
12 import Digraph
13 import qualified Prelude as P
14 import Prelude
15 import Util (sortLe)
16
17 import BlockId
18 import Bitmap
19 import CLabel
20 import Cmm hiding (blockId)
21 import CmmExpr
22 import CmmInfo
23 import CmmProcPointZ
24 import CmmStackLayout
25 import CmmTx
26 import DFMonad
27 import FastString
28 import FiniteMap
29 import ForeignCall
30 import IdInfo
31 import List (sortBy)
32 import Maybes
33 import MkZipCfg
34 import MkZipCfgCmm hiding (CmmAGraph, CmmBlock, CmmTopZ, CmmZ, CmmGraph)
35 import Monad
36 import Name
37 import Outputable
38 import Panic
39 import SMRep
40 import StgCmmClosure
41 import StgCmmForeign
42 import StgCmmMonad
43 import StgCmmUtils
44 import UniqSupply
45 import ZipCfg hiding (zip, unzip, last)
46 import qualified ZipCfg as G
47 import ZipCfgCmmRep
48 import ZipDataflow
49
50 ----------------------------------------------------------------
51 -- Building InfoTables
52
53
54 -----------------------------------------------------------------------
55 -- Stack Maps
56
57 -- Given a block ID, we return a representation of the layout of the stack,
58 -- as suspended before entering that block.
59 -- (For a return site to a function call, the layout does not include the
60 --  parameter passing area (or the "return address" on the stack)).
61 -- If the element is `Nothing`, then it represents a word of the stack that
62 -- does not contain a live pointer.
63 -- If the element is `Just` a register, then it represents a live spill slot
64 -- for a pointer; we assume that a pointer is the size of a word.
65 -- The head of the list represents the young end of the stack where the infotable
66 -- pointer for the block `Bid` is stored.
67 -- The infotable pointer itself is not included in the list.
68 -- Call areas are also excluded from the list: besides the stuff in the update
69 -- frame (and the return infotable), call areas should never be live across
70 -- function calls.
71
72 -- RTS Invariant: All pointers must be word-aligned because each bit in the bitmap
73 -- represents a word. Consequently, we have to be careful when we see a live slot
74 -- on the stack: if we have packed multiple sub-word values into a word,
75 -- we have to make sure that we only mark the entire word as a non-pointer.
76
77 -- Also, don't forget to stop at the old end of the stack (oldByte),
78 -- which may differ depending on whether there is an update frame.
79 live_ptrs :: ByteOff -> BlockEnv SubAreaSet -> AreaMap -> BlockId -> [Maybe LocalReg]
80 live_ptrs oldByte slotEnv areaMap bid =
81   -- pprTrace "live_ptrs for" (ppr bid <+> ppr youngByte <+> ppr liveSlots) $
82   reverse $ slotsToList youngByte liveSlots []
83   where slotsToList n [] results | n == oldByte = results -- at old end of stack frame
84         slotsToList n (s : _) _  | n == oldByte =
85           pprPanic "slot left off live_ptrs" (ppr s <+> ppr oldByte <+>
86                ppr n <+> ppr liveSlots <+> ppr youngByte)
87         slotsToList n _ _ | n < oldByte =
88           panic "stack slots not allocated on word boundaries?"
89         slotsToList n l@((n', r, w) : rst) results =
90           if n == (n' + w) then -- slot's young byte is at n
91             ASSERT (not (isPtr r) ||
92                     (n `mod` wORD_SIZE == 0 && w == wORD_SIZE)) -- ptrs must be aligned
93             slotsToList next (dropWhile (non_ptr_younger_than next) rst)
94                         (stack_rep : results)
95           else slotsToList next (dropWhile (non_ptr_younger_than next) l)
96                            (Nothing : results)
97           where next = n - wORD_SIZE
98                 stack_rep = if isPtr r then Just r else Nothing
99         slotsToList n [] results = slotsToList (n - wORD_SIZE) [] (Nothing : results)
100         non_ptr_younger_than next (n', r, w) =
101           n' + w > next &&
102             ASSERT (not (isPtr r))
103             True
104         isPtr = isGcPtrType . localRegType
105         liveSlots = sortBy (\ (off,_,_) (off',_,_) -> compare off' off)
106                            (foldFM (\_ -> flip $ foldl add_slot) [] slots)
107                     
108         add_slot rst (a@(RegSlot r@(LocalReg _ ty)), off, w) = 
109           if off == w && widthInBytes (typeWidth ty) == w then
110             (expectJust "add_slot" (lookupFM areaMap a), r, w) : rst
111           else panic "live_ptrs: only part of a variable live at a proc point"
112         add_slot rst (CallArea Old, _, _) =
113           rst -- the update frame (or return infotable) should be live
114               -- would be nice to check that only that part of the callarea is live...
115         add_slot rst ((CallArea _), _, _) =
116           rst
117           -- JD: THIS ISN'T CURRENTLY A CORRECTNESS PROBLEM, BUT WE SHOULD REALLY
118           -- MAKE LIVENESS INFO AROUND CALLS MORE PRECISE -- FOR NOW, A 32-BIT
119           -- FLOAT PADS OUT TO 64 BITS, BUT WE ASSUME THE WHOLE PARAMETER-PASSING
120           -- AREA IS LIVE (WHICH IT ISN'T...).  WE SHOULD JUST PUT THE LIVE AREAS
121           -- IN THE CALL NODES, WHICH SHOULD EVENTUALLY HAVE LIVE REGISTER AS WELL,
122           -- SO IT'S ALL GOING IN THE SAME DIRECTION.
123           -- pprPanic "CallAreas must not be live across function calls" (ppr bid <+> ppr c)
124         slots = expectJust "live_ptrs slots" $ lookupBlockEnv slotEnv bid
125         youngByte = expectJust "live_ptrs bid_pos" $ lookupFM areaMap (CallArea (Young bid))
126
127 -- Construct the stack maps for the given procedure.
128 setInfoTableStackMap :: SlotEnv -> AreaMap -> CmmTopForInfoTables -> CmmTopForInfoTables 
129 setInfoTableStackMap _ _ t@(NoInfoTable _) = t
130 setInfoTableStackMap slotEnv areaMap t@(FloatingInfoTable _ bid updfr_off) =
131   updInfo (const (live_ptrs updfr_off slotEnv areaMap bid)) id t
132 setInfoTableStackMap slotEnv areaMap
133      t@(ProcInfoTable (CmmProc (CmmInfo _ _ infoTbl) _ _ g@(LGraph _ _ blocks))
134                       procpoints) =
135   case blockSetToList procpoints of
136     [bid] ->
137       let oldByte = case infoTbl of
138                          CmmInfoTable _ _ _ (ContInfo _ _) -> 
139                            case lookupBlockEnv blocks bid of
140                               Just (Block _ (StackInfo {returnOff = Just n}) _) -> n
141                               _ -> pprPanic "misformed graph at procpoint" (ppr g)
142                          _ -> initUpdFrameOff -- entry to top-level function
143           stack_vars = live_ptrs oldByte slotEnv areaMap bid
144       in updInfo (const stack_vars) id t
145     _ -> panic "setInfoTableStackMap: unexpect number of procpoints"
146            -- until we stop splitting the graphs at procpoints in the native path
147 setInfoTableStackMap _ _ _ = panic "unexpected case for setInfoTableStackMap"
148 {-
149 setInfoTableStackMap slotEnv areaMap
150       (Just bid, p@(CmmProc (CmmInfo _ _ infoTbl) _ _ g@(LGraph entry _ blocks))) =
151   let oldByte = case infoTbl of
152                      CmmInfoTable _ _ _ (ContInfo _ _) -> 
153                        case lookupBlockEnv blocks bid of
154                           Just (Block _ (StackInfo {returnOff = Just n}) _) -> n
155                           _ -> pprPanic "misformed graph at procpoint" (ppr g)
156                      _ -> initUpdFrameOff -- entry to top-level function
157       stack_vars = live_ptrs oldByte slotEnv areaMap bid
158   in (Just bid, upd_info_tbl (const stack_vars) id p)
159 setInfoTableStackMap _ _ t@(_, CmmData {}) = t
160 setInfoTableStackMap _ _ _ = panic "bad args to setInfoTableStackMap"
161 -}
162
163
164 -----------------------------------------------------------------------
165 -- SRTs
166
167 -- WE NEED AN EXAMPLE HERE.
168 -- IN PARTICULAR, WE NEED TO POINT OUT THE DISTINCTION BETWEEN
169 -- FUNCTIONS WITH STATIC CLOSURES AND THOSE THAT MUST BE CONSTRUCTED
170 -- DYNAMICALLY (AND HENCE CAN'T BE REFERENCED IN AN SRT).
171 -- IN THE LATTER CASE, WE HAVE TO TAKE ALL THE CAFs REFERENCED BY
172 -- THE CLOSURE AND INLINE THEM INTO ANY SRT THAT MAY MENTION THE CLOSURE.
173 -- (I.E. TAKE THE TRANSITIVE CLOSURE, but only for non-static closures).
174
175
176 -----------------------------------------------------------------------
177 -- Finding the CAFs used by a procedure
178
179 type CAFSet = FiniteMap CLabel ()
180 type CAFEnv = BlockEnv CAFSet
181
182 -- First, an analysis to find live CAFs.
183 cafLattice :: DataflowLattice CAFSet
184 cafLattice = DataflowLattice "live cafs" emptyFM add False
185   where add new old = if sizeFM new' > sizeFM old then aTx new' else noTx new'
186           where new' = new `plusFM` old
187
188 cafTransfers :: BackwardTransfers Middle Last CAFSet
189 cafTransfers = BackwardTransfers first middle last
190     where first  live _ = live
191           middle live m = foldExpDeepMiddle addCaf m live
192           last   env  l = foldExpDeepLast addCaf l (joinOuts cafLattice env l)
193           addCaf e set = case e of
194                  CmmLit (CmmLabel c)              -> add c set
195                  CmmLit (CmmLabelOff c _)         -> add c set
196                  CmmLit (CmmLabelDiffOff c1 c2 _) -> add c1 $ add c2 set
197                  _ -> set
198           add l s = if hasCAF l then addToFM s (cvtToClosureLbl l) () else s
199
200 type CafFix a = FuelMonad (BackwardFixedPoint Middle Last CAFSet a)
201 cafAnal :: LGraph Middle Last -> FuelMonad CAFEnv
202 cafAnal g = liftM zdfFpFacts (res :: CafFix ())
203   where res = zdfSolveFromL emptyBlockEnv "live CAF analysis" cafLattice
204                             cafTransfers (fact_bot cafLattice) g
205
206 -----------------------------------------------------------------------
207 -- Building the SRTs
208
209 -- Description of the SRT for a given module.
210 -- Note that this SRT may grow as we greedily add new CAFs to it.
211 data TopSRT = TopSRT { lbl      :: CLabel
212                      , next_elt :: Int -- the next entry in the table
213                      , rev_elts :: [CLabel]
214                      , elt_map  :: FiniteMap CLabel Int }
215                         -- map: CLabel -> its last entry in the table
216 instance Outputable TopSRT where
217   ppr (TopSRT lbl next elts eltmap) =
218     text "TopSRT:" <+> ppr lbl <+> ppr next <+> ppr elts <+> ppr eltmap
219
220 emptySRT :: MonadUnique m => m TopSRT
221 emptySRT =
222   do top_lbl <- getUniqueM >>= \ u -> return $ mkSRTLabel (mkFCallName u "srt") NoCafRefs
223      return TopSRT { lbl = top_lbl, next_elt = 0, rev_elts = [], elt_map = emptyFM }
224
225 cafMember :: TopSRT -> CLabel -> Bool
226 cafMember srt lbl = elemFM lbl (elt_map srt)
227
228 cafOffset :: TopSRT -> CLabel -> Maybe Int
229 cafOffset srt lbl = lookupFM (elt_map srt) lbl
230
231 addCAF :: CLabel -> TopSRT -> TopSRT
232 addCAF caf srt =
233   srt { next_elt = last + 1
234       , rev_elts = caf : rev_elts srt
235       , elt_map  = addToFM (elt_map srt) caf last }
236     where last  = next_elt srt
237
238 srtToData :: TopSRT -> CmmZ
239 srtToData srt = Cmm [CmmData RelocatableReadOnlyData (CmmDataLabel (lbl srt) : tbl)]
240     where tbl = map (CmmStaticLit . CmmLabel) (reverse (rev_elts srt))
241
242 -- Once we have found the CAFs, we need to do two things:
243 -- 1. Build a table of all the CAFs used in the procedure.
244 -- 2. Compute the C_SRT describing the subset of CAFs live at each procpoint.
245 --
246 -- When building the local view of the SRT, we first make sure that all the CAFs are 
247 -- in the SRT. Then, if the number of CAFs is small enough to fit in a bitmap,
248 -- we make sure they're all close enough to the bottom of the table that the
249 -- bitmap will be able to cover all of them.
250 buildSRTs :: TopSRT -> FiniteMap CLabel CAFSet -> CAFSet ->
251              FuelMonad (TopSRT, Maybe CmmTopZ, C_SRT)
252 buildSRTs topSRT topCAFMap cafs =
253   do let liftCAF lbl () z = -- get CAFs for functions without static closures
254            case lookupFM topCAFMap lbl of Just cafs -> z `plusFM` cafs
255                                           Nothing   -> addToFM z lbl ()
256          sub_srt topSRT localCafs =
257            let cafs = keysFM (foldFM liftCAF emptyFM localCafs)
258                mkSRT topSRT =
259                  do localSRTs <- procpointSRT (lbl topSRT) (elt_map topSRT) cafs
260                     return (topSRT, localSRTs)
261            in if length cafs > maxBmpSize then
262                 mkSRT (foldl add_if_missing topSRT cafs)
263               else -- make sure all the cafs are near the bottom of the srt
264                 mkSRT (add_if_too_far topSRT cafs)
265          add_if_missing srt caf =
266            if cafMember srt caf then srt else addCAF caf srt
267          -- If a CAF is more than maxBmpSize entries from the young end of the
268          -- SRT, then we add it to the SRT again.
269          -- (Note: Not in the SRT => infinitely far.)
270          add_if_too_far srt@(TopSRT {elt_map = m}) cafs =
271            add srt (sortBy farthestFst cafs)
272              where
273                farthestFst x y = case (lookupFM m x, lookupFM m y) of
274                                    (Nothing, Nothing) -> EQ
275                                    (Nothing, Just _)  -> LT
276                                    (Just _,  Nothing) -> GT
277                                    (Just d, Just d')  -> compare d' d
278                add srt [] = srt
279                add srt@(TopSRT {next_elt = next}) (caf : rst) =
280                  case cafOffset srt caf of
281                    Just ix -> if next - ix > maxBmpSize then
282                                 add (addCAF caf srt) rst
283                               else srt
284                    Nothing -> add (addCAF caf srt) rst
285      (topSRT, subSRTs) <- sub_srt topSRT cafs
286      let (sub_tbls, blockSRTs) = subSRTs
287      return (topSRT, sub_tbls, blockSRTs)
288
289 -- Construct an SRT bitmap.
290 -- Adapted from simpleStg/SRT.lhs, which expects Id's.
291 procpointSRT :: CLabel -> FiniteMap CLabel Int -> [CLabel] ->
292                 FuelMonad (Maybe CmmTopZ, C_SRT)
293 procpointSRT _ _ [] =
294  return (Nothing, NoC_SRT)
295 procpointSRT top_srt top_table entries =
296  do (top, srt) <- bitmap `seq` to_SRT top_srt offset len bitmap
297     return (top, srt)
298   where
299     ints = map (expectJust "constructSRT" . lookupFM top_table) entries
300     sorted_ints = sortLe (<=) ints
301     offset = head sorted_ints
302     bitmap_entries = map (subtract offset) sorted_ints
303     len = P.last bitmap_entries + 1
304     bitmap = intsToBitmap len bitmap_entries
305
306 maxBmpSize :: Int
307 maxBmpSize = widthInBits wordWidth `div` 2
308
309 -- Adapted from codeGen/StgCmmUtils, which converts from SRT to C_SRT.
310 to_SRT :: CLabel -> Int -> Int -> Bitmap -> FuelMonad (Maybe CmmTopZ, C_SRT)
311 to_SRT top_srt off len bmp
312   | len > maxBmpSize || bmp == [fromIntegral srt_escape]
313   = do id <- getUniqueM
314        let srt_desc_lbl = mkLargeSRTLabel id
315            tbl = CmmData RelocatableReadOnlyData $
316                    CmmDataLabel srt_desc_lbl : map CmmStaticLit
317                      ( cmmLabelOffW top_srt off
318                      : mkWordCLit (fromIntegral len)
319                      : map mkWordCLit bmp)
320        return (Just tbl, C_SRT srt_desc_lbl 0 srt_escape)
321   | otherwise
322   = return (Nothing, C_SRT top_srt off (fromIntegral (head bmp)))
323         -- The fromIntegral converts to StgHalfWord
324
325 -- Gather CAF info for a procedure, but only if the procedure
326 -- doesn't have a static closure.
327 -- (If it has a static closure, it will already have an SRT to
328 --  keep its CAFs live.)
329 -- Any procedure referring to a non-static CAF c must keep live the
330 -- any CAF that is reachable from c.
331 localCAFInfo :: CAFEnv -> CmmTopZ -> Maybe (CLabel, CAFSet)
332 localCAFInfo _      (CmmData _ _) = Nothing
333 localCAFInfo cafEnv (CmmProc (CmmInfo _ _ infoTbl) top_l _ (LGraph entry _ _)) =
334   case infoTbl of
335     CmmInfoTable False _ _ _ ->
336       Just (cvtToClosureLbl top_l,
337             expectJust "maybeBindCAFs" $ lookupBlockEnv cafEnv entry)
338     _ -> Nothing
339
340 -- Once we have the local CAF sets for some (possibly) mutually
341 -- recursive functions, we can create an environment mapping
342 -- each function to its set of CAFs. Note that a CAF may
343 -- be a reference to a function. If that function f does not have
344 -- a static closure, then we need to refer specifically
345 -- to the set of CAFs used by f. Of course, the set of CAFs
346 -- used by f must be included in the local CAF sets that are input to
347 -- this function. To minimize lookup time later, we return
348 -- the environment with every reference to f replaced by its set of CAFs.
349 -- To do this replacement efficiently, we gather strongly connected
350 -- components, then we sort the components in topological order.
351 mkTopCAFInfo :: [(CLabel, CAFSet)] -> FiniteMap CLabel CAFSet
352 mkTopCAFInfo localCAFs = foldl addToTop emptyFM g
353   where addToTop env (AcyclicSCC (l, cafset)) =
354           addToFM env l (flatten env cafset)
355         addToTop env (CyclicSCC nodes) =
356           let (lbls, cafsets) = unzip nodes
357               cafset  = foldl plusFM  emptyFM cafsets `delListFromFM` lbls
358           in foldl (\env l -> addToFM env l (flatten env cafset)) env lbls
359         flatten env cafset = foldFM (lookup env) emptyFM cafset
360         lookup env caf () cafset' =
361           case lookupFM env caf of Just cafs -> foldFM add cafset' cafs
362                                    Nothing -> add caf () cafset'
363         add caf () cafset' = addToFM cafset' caf ()
364         g = stronglyConnCompFromEdgedVertices
365               (map (\n@(l, cafs) -> (n, l, keysFM cafs)) localCAFs)
366
367 type StackLayout = [Maybe LocalReg]
368
369 -- Bundle the CAFs used at a procpoint.
370 bundleCAFs :: CAFEnv -> CmmTopForInfoTables -> (CAFSet, CmmTopForInfoTables)
371 bundleCAFs cafEnv t@(ProcInfoTable _ procpoints) =
372   case blockSetToList procpoints of
373     [bid] -> (expectJust "bundleCAFs " (lookupBlockEnv cafEnv bid), t)
374     _     -> panic "setInfoTableStackMap: unexpect number of procpoints"
375              -- until we stop splitting the graphs at procpoints in the native path
376 bundleCAFs cafEnv t@(FloatingInfoTable _ bid _) =
377   (expectJust "bundleCAFs " (lookupBlockEnv cafEnv bid), t)
378 bundleCAFs _ t@(NoInfoTable _) = (emptyFM, t)
379
380 -- Construct the SRTs for the given procedure.
381 setInfoTableSRT :: FiniteMap CLabel CAFSet -> TopSRT -> (CAFSet, CmmTopForInfoTables) ->
382                    FuelMonad (TopSRT, [CmmTopForInfoTables])
383 setInfoTableSRT topCAFMap topSRT (cafs, t@(ProcInfoTable _ procpoints)) =
384   case blockSetToList procpoints of
385     [_] -> setSRT cafs topCAFMap topSRT t
386     _   -> panic "setInfoTableStackMap: unexpect number of procpoints"
387            -- until we stop splitting the graphs at procpoints in the native path
388 setInfoTableSRT topCAFMap topSRT (cafs, t@(FloatingInfoTable _ _ _)) =
389   setSRT cafs topCAFMap topSRT t
390 setInfoTableSRT _ topSRT (_, t@(NoInfoTable _)) = return (topSRT, [t])
391
392 setSRT :: CAFSet -> FiniteMap CLabel CAFSet -> TopSRT ->
393           CmmTopForInfoTables -> FuelMonad (TopSRT, [CmmTopForInfoTables])
394 setSRT cafs topCAFMap topSRT t =
395   do (topSRT, cafTable, srt) <- buildSRTs topSRT topCAFMap cafs
396      let t' = updInfo id (const srt) t
397      case cafTable of
398        Just tbl -> return (topSRT, [t', NoInfoTable tbl])
399        Nothing  -> return (topSRT, [t'])
400
401 updInfo :: (StackLayout -> StackLayout) -> (C_SRT -> C_SRT) ->
402            CmmTopForInfoTables -> CmmTopForInfoTables 
403 updInfo toVars toSrt (ProcInfoTable (CmmProc info top_l top_args g) procpoints) =
404   ProcInfoTable (CmmProc (updInfoTbl toVars toSrt info) top_l top_args g) procpoints
405 updInfo toVars toSrt (FloatingInfoTable info bid updfr_off) =
406   FloatingInfoTable (updInfoTbl toVars toSrt info) bid updfr_off
407 updInfo _ _ (NoInfoTable _) = panic "can't update NoInfoTable"
408 updInfo _ _ _ = panic "unexpected arg to updInfo"
409
410 updInfoTbl :: (StackLayout -> StackLayout) -> (C_SRT -> C_SRT) -> CmmInfo -> CmmInfo 
411 updInfoTbl toVars toSrt (CmmInfo gc upd_fr (CmmInfoTable s p t typeinfo))
412   = CmmInfo gc upd_fr (CmmInfoTable s p t typeinfo')
413     where typeinfo' = case typeinfo of
414             t@(ConstrInfo _ _ _)    -> t
415             (FunInfo    c s a d e)  -> FunInfo c (toSrt s) a d e
416             (ThunkInfo  c s)        -> ThunkInfo c (toSrt s)
417             (ThunkSelectorInfo x s) -> ThunkSelectorInfo x (toSrt s)
418             (ContInfo v s)          -> ContInfo (toVars v) (toSrt s)
419 updInfoTbl _ _ t@(CmmInfo _ _ CmmNonInfoTable) = t
420   
421 -- Lower the CmmTopForInfoTables type down to good old CmmTopZ
422 -- by emitting info tables as data where necessary.
423 finishInfoTables :: CmmTopForInfoTables -> IO [CmmTopZ]
424 finishInfoTables (NoInfoTable t) = return [t]
425 finishInfoTables (ProcInfoTable p _) = return [p]
426 finishInfoTables (FloatingInfoTable (CmmInfo _ _ infotbl) bid _) =
427   do uniq_supply <- mkSplitUniqSupply 'i'
428      return $ mkBareInfoTable (retPtLbl bid) (uniqFromSupply uniq_supply) infotbl
429
430 ----------------------------------------------------------------
431 -- Safe foreign calls:
432 -- Our analyses capture the dataflow facts at block boundaries, but we need
433 -- to extend the CAF and live-slot analyses to safe foreign calls as well,
434 -- which show up as middle nodes.
435 extendEnvsForSafeForeignCalls :: CAFEnv -> SlotEnv -> CmmGraph -> (CAFEnv, SlotEnv)
436 extendEnvsForSafeForeignCalls cafEnv slotEnv g =
437   fold_blocks block (cafEnv, slotEnv) g
438     where block b z =
439             tail ( bt_last_in cafTransfers      (lookupFn cafEnv)  l
440                  , bt_last_in liveSlotTransfers (lookupFn slotEnv) l)
441                  z head
442              where (head, last) = goto_end (G.unzip b)
443                    l = case last of LastOther l -> l
444                                     LastExit -> panic "extendEnvs lastExit"
445           tail _ z (ZFirst _ _) = z
446           tail lives@(cafs, slots) (cafEnv, slotEnv)
447                (ZHead h m@(MidForeignCall (Safe bid _) _ _ _)) =
448             let slots'   = removeLiveSlotDefs slots m
449                 slotEnv' = extendBlockEnv slotEnv bid slots'
450                 cafEnv'  = extendBlockEnv cafEnv  bid cafs
451             in  tail (upd lives m) (cafEnv', slotEnv') h
452           tail lives z (ZHead h m) = tail (upd lives m) z h
453           lookupFn map k = expectJust "extendEnvsForSafeFCalls" $ lookupBlockEnv map k
454           upd (cafs, slots) m =
455             (bt_middle_in cafTransfers cafs m, bt_middle_in liveSlotTransfers slots m)
456
457 -- Safe foreign calls: We need to insert the code that suspends and resumes
458 -- the thread before and after a safe foreign call.
459 -- Why do we do this so late in the pipeline?
460 -- Because we need this code to appear without interrruption: you can't rely on the
461 -- value of the stack pointer between the call and resetting the thread state;
462 -- you need to have an infotable on the young end of the stack both when
463 -- suspending the thread and making the foreign call.
464 -- All of this is much easier if we insert the suspend and resume calls here.
465
466 -- At the same time, we prepare for the stages of the compiler that
467 -- build the proc points. We have to do this at the same time because
468 -- the safe foreign calls need special treatment with respect to infotables.
469 -- A safe foreign call needs an infotable even though it isn't
470 -- a procpoint. The following datatype captures the information
471 -- needed to generate the infotables along with the Cmm data and procedures.
472
473 data CmmTopForInfoTables
474   = NoInfoTable       CmmTopZ  -- must be CmmData
475   | ProcInfoTable     CmmTopZ BlockSet -- CmmProc; argument is its set of procpoints
476   | FloatingInfoTable CmmInfo BlockId UpdFrameOffset
477 instance Outputable CmmTopForInfoTables where
478   ppr (NoInfoTable t) = text "NoInfoTable: " <+> ppr t
479   ppr (ProcInfoTable t bids) = text "ProcInfoTable: " <+> ppr t <+> ppr bids
480   ppr (FloatingInfoTable info bid upd) =
481     text "FloatingInfoTable: " <+> ppr info <+> ppr bid <+> ppr upd
482
483 -- The `safeState' record collects the info we update while lowering the
484 -- safe foreign calls in the graph.
485 data SafeState = State { s_blocks    :: BlockEnv CmmBlock
486                        , s_pps       :: ProcPointSet
487                        , s_safeCalls :: [CmmTopForInfoTables]}
488
489 lowerSafeForeignCalls
490   :: [[CmmTopForInfoTables]] -> CmmTopZ -> FuelMonad [[CmmTopForInfoTables]]
491 lowerSafeForeignCalls rst t@(CmmData _ _) = return $ [NoInfoTable t] : rst
492 lowerSafeForeignCalls rst (CmmProc info l args g@(LGraph entry off _)) = do
493   let init = return $ State emptyBlockEnv emptyBlockSet []
494   let block b@(Block bid _ _) z = do
495         state@(State {s_pps = ppset, s_blocks = blocks}) <- z
496         let ppset' = if bid == entry then extendBlockSet ppset bid else ppset
497             state' = state { s_pps = ppset' }
498         if hasSafeForeignCall b
499          then lowerSafeCallBlock state' b
500          else return (state' { s_blocks = insertBlock b blocks })
501   State blocks' g_procpoints safeCalls <- fold_blocks block init g
502   return $ safeCalls
503            : [ProcInfoTable (CmmProc info l args (LGraph entry off blocks')) g_procpoints]
504            : rst
505
506 -- Check for foreign calls -- if none, then we can avoid copying the block.
507 hasSafeForeignCall :: CmmBlock -> Bool
508 hasSafeForeignCall (Block _ _ t) = tail t
509   where tail (ZTail (MidForeignCall (Safe _ _) _ _ _) _) = True
510         tail (ZTail _ t) = tail t
511         tail (ZLast _)   = False
512
513 -- Lower each safe call in the block, update the CAF and slot environments
514 -- to include each of those calls, and insert the new block in the blockEnv.
515 lowerSafeCallBlock :: SafeState-> CmmBlock -> FuelMonad SafeState
516 lowerSafeCallBlock state b = tail (return state) (ZBlock head (ZLast last))
517   where (head, last) = goto_end (G.unzip b)
518         tail s b@(ZBlock (ZFirst _ _) _) =
519           do state <- s
520              return $ state { s_blocks = insertBlock (G.zip b) (s_blocks state) }
521         tail  s (ZBlock (ZHead h m@(MidForeignCall (Safe bid updfr_off) _ _ _)) t) =
522           do state <- s
523              let state' = state
524                    { s_safeCalls = FloatingInfoTable emptyContInfoTable bid updfr_off :
525                                      s_safeCalls state }
526              (state'', t') <- lowerSafeForeignCall state' m t
527              tail (return state'') (ZBlock h t')
528         tail s (ZBlock (ZHead h m) t) = tail s (ZBlock h (ZTail m t))
529            
530
531 -- Late in the code generator, we want to insert the code necessary
532 -- to lower a safe foreign call to a sequence of unsafe calls.
533 lowerSafeForeignCall ::
534   SafeState -> Middle -> ZTail Middle Last -> FuelMonad (SafeState, ZTail Middle Last)
535 lowerSafeForeignCall state m@(MidForeignCall (Safe infotable _) _ _ _) tail = do
536     let newTemp rep = getUniqueM >>= \u -> return (LocalReg u rep)
537     -- Both 'id' and 'new_base' are KindNonPtr because they're
538     -- RTS-only objects and are not subject to garbage collection
539     id <- newTemp bWord
540     new_base <- newTemp (cmmRegType (CmmGlobal BaseReg))
541     let (caller_save, caller_load) = callerSaveVolatileRegs 
542     load_tso <- newTemp gcWord -- TODO FIXME NOW
543     let suspendThread = CmmLit (CmmLabel (mkRtsCodeLabel (sLit "suspendThread")))
544         resumeThread  = CmmLit (CmmLabel (mkRtsCodeLabel (sLit "resumeThread")))
545         suspend = mkStore (CmmReg spReg) (CmmLit (CmmBlock infotable)) <*>
546                   saveThreadState <*>
547                   caller_save <*>
548                   mkUnsafeCall (ForeignTarget suspendThread
549                                   (ForeignConvention CCallConv [AddrHint] [AddrHint]))
550                     [id] [CmmReg (CmmGlobal BaseReg)]
551         resume = mkUnsafeCall (ForeignTarget resumeThread
552                                   (ForeignConvention CCallConv [AddrHint] [AddrHint]))
553                     [new_base] [CmmReg (CmmLocal id)] <*>
554                  -- Assign the result to BaseReg: we
555                  -- might now have a different Capability!
556                  mkAssign (CmmGlobal BaseReg) (CmmReg (CmmLocal new_base)) <*>
557                  caller_load <*>
558                  loadThreadState load_tso
559     Graph tail' blocks' <-
560       liftUniq (graphOfAGraph (suspend <*> mkMiddle m <*> resume <*> mkZTail tail))
561     return (state {s_blocks = s_blocks state `plusBlockEnv` blocks'}, tail')
562 lowerSafeForeignCall _ _ _ = panic "lowerSafeForeignCall was passed something else"