173b79971c1fac03e4c784cbda9f00200b8f2ef5
[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, off, w) =
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 c@((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 info 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 entry _ 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   -- This is surely the wrong way to get names, as in BlockId
254   do top_lbl <- getUniqueM >>= \ u -> return $ mkSRTLabel (mkFCallName u "srt") NoCafRefs
255      let liftCAF lbl () z = -- get CAFs for functions without static closures
256            case lookupFM topCAFMap lbl of Just cafs -> z `plusFM` cafs
257                                           Nothing   -> addToFM z lbl ()
258          sub_srt topSRT localCafs =
259            let cafs = keysFM (foldFM liftCAF emptyFM localCafs)
260                mkSRT topSRT =
261                  do localSRTs <- procpointSRT (lbl topSRT) (elt_map topSRT) cafs
262                     return (topSRT, localSRTs)
263            in if length cafs > maxBmpSize then
264                 mkSRT (foldl add_if_missing topSRT cafs)
265               else -- make sure all the cafs are near the bottom of the srt
266                 mkSRT (add_if_too_far topSRT cafs)
267          add_if_missing srt caf =
268            if cafMember srt caf then srt else addCAF caf srt
269          -- If a CAF is more than maxBmpSize entries from the young end of the
270          -- SRT, then we add it to the SRT again.
271          -- (Note: Not in the SRT => infinitely far.)
272          add_if_too_far srt@(TopSRT {elt_map = m}) cafs =
273            add srt (sortBy farthestFst cafs)
274              where
275                farthestFst x y = case (lookupFM m x, lookupFM m y) of
276                                    (Nothing, Nothing) -> EQ
277                                    (Nothing, Just _)  -> LT
278                                    (Just _,  Nothing) -> GT
279                                    (Just d, Just d')  -> compare d' d
280                add srt [] = srt
281                add srt@(TopSRT {next_elt = next}) (caf : rst) =
282                  case cafOffset srt caf of
283                    Just ix -> if next - ix > maxBmpSize then
284                                 add (addCAF caf srt) rst
285                               else srt
286                    Nothing -> add (addCAF caf srt) rst
287      (topSRT, subSRTs) <- sub_srt topSRT cafs
288      let (sub_tbls, blockSRTs) = subSRTs
289      return (topSRT, sub_tbls, blockSRTs)
290
291 -- Construct an SRT bitmap.
292 -- Adapted from simpleStg/SRT.lhs, which expects Id's.
293 procpointSRT :: CLabel -> FiniteMap CLabel Int -> [CLabel] ->
294                 FuelMonad (Maybe CmmTopZ, C_SRT)
295 procpointSRT top_srt top_table [] =
296  return (Nothing, NoC_SRT)
297 procpointSRT top_srt top_table entries =
298  do (top, srt) <- bitmap `seq` to_SRT top_srt offset len bitmap
299     return (top, srt)
300   where
301     ints = map (expectJust "constructSRT" . lookupFM top_table) entries
302     sorted_ints = sortLe (<=) ints
303     offset = head sorted_ints
304     bitmap_entries = map (subtract offset) sorted_ints
305     len = P.last bitmap_entries + 1
306     bitmap = intsToBitmap len bitmap_entries
307
308 maxBmpSize :: Int
309 maxBmpSize = widthInBits wordWidth `div` 2
310
311 -- Adapted from codeGen/StgCmmUtils, which converts from SRT to C_SRT.
312 to_SRT :: CLabel -> Int -> Int -> Bitmap -> FuelMonad (Maybe CmmTopZ, C_SRT)
313 to_SRT top_srt off len bmp
314   | len > maxBmpSize || bmp == [fromIntegral srt_escape]
315   = do id <- getUniqueM
316        let srt_desc_lbl = mkLargeSRTLabel id
317            tbl = CmmData RelocatableReadOnlyData $
318                    CmmDataLabel srt_desc_lbl : map CmmStaticLit
319                      ( cmmLabelOffW top_srt off
320                      : mkWordCLit (fromIntegral len)
321                      : map mkWordCLit bmp)
322        return (Just tbl, C_SRT srt_desc_lbl 0 srt_escape)
323   | otherwise
324   = return (Nothing, C_SRT top_srt off (fromIntegral (head bmp)))
325         -- The fromIntegral converts to StgHalfWord
326
327 -- Gather CAF info for a procedure, but only if the procedure
328 -- doesn't have a static closure.
329 -- (If it has a static closure, it will already have an SRT to
330 --  keep its CAFs live.)
331 -- Any procedure referring to a non-static CAF c must keep live the
332 -- any CAF that is reachable from c.
333 localCAFInfo :: CAFEnv -> CmmTopZ -> Maybe (CLabel, CAFSet)
334 localCAFInfo _    t@(CmmData _ _) = Nothing
335 localCAFInfo cafEnv (CmmProc (CmmInfo _ _ infoTbl) top_l _ (LGraph entry _ _)) =
336   case infoTbl of
337     CmmInfoTable False _ _ _ ->
338       Just (cvtToClosureLbl top_l,
339             expectJust "maybeBindCAFs" $ lookupBlockEnv cafEnv entry)
340     _ -> Nothing
341
342 -- Once we have the local CAF sets for some (possibly) mutually
343 -- recursive functions, we can create an environment mapping
344 -- each function to its set of CAFs. Note that a CAF may
345 -- be a reference to a function. If that function f does not have
346 -- a static closure, then we need to refer specifically
347 -- to the set of CAFs used by f. Of course, the set of CAFs
348 -- used by f must be included in the local CAF sets that are input to
349 -- this function. To minimize lookup time later, we return
350 -- the environment with every reference to f replaced by its set of CAFs.
351 -- To do this replacement efficiently, we gather strongly connected
352 -- components, then we sort the components in topological order.
353 mkTopCAFInfo :: [(CLabel, CAFSet)] -> FiniteMap CLabel CAFSet
354 mkTopCAFInfo localCAFs = foldl addToTop emptyFM g
355   where addToTop env (AcyclicSCC (l, cafset)) =
356           addToFM env l (flatten env cafset)
357         addToTop env (CyclicSCC nodes) =
358           let (lbls, cafsets) = unzip nodes
359               cafset  = foldl plusFM  emptyFM cafsets `delListFromFM` lbls
360           in foldl (\env l -> addToFM env l (flatten env cafset)) env lbls
361         flatten env cafset = foldFM (lookup env) emptyFM cafset
362         lookup env caf () cafset' =
363           case lookupFM env caf of Just cafs -> foldFM add cafset' cafs
364                                    Nothing -> add caf () cafset'
365         add caf () cafset' = addToFM cafset' caf ()
366         g = stronglyConnCompFromEdgedVertices
367               (map (\n@(l, cafs) -> (n, l, keysFM cafs)) localCAFs)
368
369 type StackLayout = [Maybe LocalReg]
370
371 -- Bundle the CAFs used at a procpoint.
372 bundleCAFs :: CAFEnv -> CmmTopForInfoTables -> (CAFSet, CmmTopForInfoTables)
373 bundleCAFs cafEnv t@(ProcInfoTable _ procpoints) =
374   case blockSetToList procpoints of
375     [bid] -> (expectJust "bundleCAFs " (lookupBlockEnv cafEnv bid), t)
376     _     -> panic "setInfoTableStackMap: unexpect number of procpoints"
377              -- until we stop splitting the graphs at procpoints in the native path
378 bundleCAFs cafEnv t@(FloatingInfoTable _ bid _) =
379   (expectJust "bundleCAFs " (lookupBlockEnv cafEnv bid), t)
380 bundleCAFs _ t@(NoInfoTable _) = (emptyFM, t)
381
382 -- Construct the SRTs for the given procedure.
383 setInfoTableSRT :: FiniteMap CLabel CAFSet -> TopSRT -> (CAFSet, CmmTopForInfoTables) ->
384                    FuelMonad (TopSRT, [CmmTopForInfoTables])
385 setInfoTableSRT topCAFMap topSRT (cafs, t@(ProcInfoTable p procpoints)) =
386   case blockSetToList procpoints of
387     [bid] -> setSRT cafs topCAFMap topSRT t
388     _     -> panic "setInfoTableStackMap: unexpect number of procpoints"
389              -- until we stop splitting the graphs at procpoints in the native path
390 setInfoTableSRT topCAFMap topSRT (cafs, t@(FloatingInfoTable info bid _)) =
391   setSRT cafs topCAFMap topSRT t
392 setInfoTableSRT _ topSRT (_, t@(NoInfoTable _)) = return (topSRT, [t])
393
394 setSRT :: CAFSet -> FiniteMap CLabel CAFSet -> TopSRT ->
395           CmmTopForInfoTables -> FuelMonad (TopSRT, [CmmTopForInfoTables])
396 setSRT cafs topCAFMap topSRT t =
397   do (topSRT, cafTable, srt) <- buildSRTs topSRT topCAFMap cafs
398      let t' = updInfo id (const srt) t
399      case cafTable of
400        Just tbl -> return (topSRT, [t', NoInfoTable tbl])
401        Nothing  -> return (topSRT, [t'])
402
403 updInfo :: (StackLayout -> StackLayout) -> (C_SRT -> C_SRT) ->
404            CmmTopForInfoTables -> CmmTopForInfoTables 
405 updInfo toVars toSrt (ProcInfoTable (CmmProc info top_l top_args g) procpoints) =
406   ProcInfoTable (CmmProc (updInfoTbl toVars toSrt info) top_l top_args g) procpoints
407 updInfo toVars toSrt (FloatingInfoTable info bid updfr_off) =
408   FloatingInfoTable (updInfoTbl toVars toSrt info) bid updfr_off
409 updInfo toVars toSrt (NoInfoTable _) = panic "can't update NoInfoTable"
410 updInfo _ _ _ = panic "unexpected arg to updInfo"
411
412 updInfoTbl :: (StackLayout -> StackLayout) -> (C_SRT -> C_SRT) -> CmmInfo -> CmmInfo 
413 updInfoTbl toVars toSrt (CmmInfo gc upd_fr (CmmInfoTable s p t typeinfo))
414   = CmmInfo gc upd_fr (CmmInfoTable s p t typeinfo')
415     where typeinfo' = case typeinfo of
416             t@(ConstrInfo _ _ _)    -> t
417             (FunInfo    c s a d e)  -> FunInfo c (toSrt s) a d e
418             (ThunkInfo  c s)        -> ThunkInfo c (toSrt s)
419             (ThunkSelectorInfo x s) -> ThunkSelectorInfo x (toSrt s)
420             (ContInfo v s)          -> ContInfo (toVars v) (toSrt s)
421 updInfoTbl toVars toSrt t@(CmmInfo _ _ CmmNonInfoTable) = t
422   
423 -- Lower the CmmTopForInfoTables type down to good old CmmTopZ
424 -- by emitting info tables as data where necessary.
425 finishInfoTables :: CmmTopForInfoTables -> IO [CmmTopZ]
426 finishInfoTables (NoInfoTable t) = return [t]
427 finishInfoTables (ProcInfoTable p _) = return [p]
428 finishInfoTables (FloatingInfoTable (CmmInfo _ _ infotbl) bid _) =
429   do uniq_supply <- mkSplitUniqSupply 'i'
430      return $ mkBareInfoTable (retPtLbl bid) (uniqFromSupply uniq_supply) infotbl
431
432 ----------------------------------------------------------------
433 -- Safe foreign calls:
434 -- Our analyses capture the dataflow facts at block boundaries, but we need
435 -- to extend the CAF and live-slot analyses to safe foreign calls as well,
436 -- which show up as middle nodes.
437 extendEnvsForSafeForeignCalls :: CAFEnv -> SlotEnv -> CmmGraph -> (CAFEnv, SlotEnv)
438 extendEnvsForSafeForeignCalls cafEnv slotEnv g =
439   fold_blocks block (cafEnv, slotEnv) g
440     where block b@(Block _ _ t) z =
441             tail ( bt_last_in cafTransfers      (lookupFn cafEnv)  l
442                  , bt_last_in liveSlotTransfers (lookupFn slotEnv) l)
443                  z head
444              where (head, last) = goto_end (G.unzip b)
445                    l = case last of LastOther l -> l
446                                     LastExit -> panic "extendEnvs lastExit"
447           tail lives z (ZFirst _ _) = z
448           tail lives@(cafs, slots) (cafEnv, slotEnv)
449                (ZHead h m@(MidForeignCall (Safe bid updfr_off) _ _ _)) =
450             let slots'   = removeLiveSlotDefs slots m
451                 slotEnv' = extendBlockEnv slotEnv bid slots'
452                 cafEnv'  = extendBlockEnv cafEnv  bid cafs
453             in  tail (upd lives m) (cafEnv', slotEnv') h
454           tail lives z (ZHead h m) = tail (upd lives m) z h
455           lookupFn map k = expectJust "extendEnvsForSafeFCalls" $ lookupBlockEnv map k
456           upd (cafs, slots) m =
457             (bt_middle_in cafTransfers cafs m, bt_middle_in liveSlotTransfers slots m)
458
459 -- Safe foreign calls: We need to insert the code that suspends and resumes
460 -- the thread before and after a safe foreign call.
461 -- Why do we do this so late in the pipeline?
462 -- Because we need this code to appear without interrruption: you can't rely on the
463 -- value of the stack pointer between the call and resetting the thread state;
464 -- you need to have an infotable on the young end of the stack both when
465 -- suspending the thread and making the foreign call.
466 -- All of this is much easier if we insert the suspend and resume calls here.
467
468 -- At the same time, we prepare for the stages of the compiler that
469 -- build the proc points. We have to do this at the same time because
470 -- the safe foreign calls need special treatment with respect to infotables.
471 -- A safe foreign call needs an infotable even though it isn't
472 -- a procpoint. The following datatype captures the information
473 -- needed to generate the infotables along with the Cmm data and procedures.
474
475 data CmmTopForInfoTables
476   = NoInfoTable       CmmTopZ  -- must be CmmData
477   | ProcInfoTable     CmmTopZ BlockSet -- CmmProc; argument is its set of procpoints
478   | FloatingInfoTable CmmInfo BlockId UpdFrameOffset
479 instance Outputable CmmTopForInfoTables where
480   ppr (NoInfoTable t) = text "NoInfoTable: " <+> ppr t
481   ppr (ProcInfoTable t bids) = text "ProcInfoTable: " <+> ppr t <+> ppr bids
482   ppr (FloatingInfoTable info bid upd) =
483     text "FloatingInfoTable: " <+> ppr info <+> ppr bid <+> ppr upd
484
485 -- The `safeState' record collects the info we update while lowering the
486 -- safe foreign calls in the graph.
487 data SafeState = State { s_blocks    :: BlockEnv CmmBlock
488                        , s_pps       :: ProcPointSet
489                        , s_safeCalls :: [CmmTopForInfoTables]}
490
491 lowerSafeForeignCalls
492   :: ProcPointSet ->           [[CmmTopForInfoTables]] ->
493           CmmTopZ -> FuelMonad [[CmmTopForInfoTables]]
494 lowerSafeForeignCalls _ rst t@(CmmData _ _) = return $ [NoInfoTable t] : rst
495 lowerSafeForeignCalls procpoints rst
496                       t@(CmmProc info l args g@(LGraph entry off blocks)) = do
497   let init = return $ State emptyBlockEnv emptyBlockSet []
498   let block b@(Block bid _ _) z = do
499         state@(State {s_pps = ppset, s_blocks = blocks}) <- z
500         let ppset' = if bid == entry then extendBlockSet ppset bid else ppset
501             state' = state { s_pps = ppset' }
502         if hasSafeForeignCall b
503          then lowerSafeCallBlock state' b
504          else return (state' { s_blocks = insertBlock b blocks })
505   State blocks' g_procpoints safeCalls <- fold_blocks block init g
506   return $ safeCalls
507            : [ProcInfoTable (CmmProc info l args (LGraph entry off blocks')) g_procpoints]
508            : rst
509
510 -- Check for foreign calls -- if none, then we can avoid copying the block.
511 hasSafeForeignCall :: CmmBlock -> Bool
512 hasSafeForeignCall (Block _ _ t) = tail t
513   where tail (ZTail (MidForeignCall (Safe _ _) _ _ _) t) = True
514         tail (ZTail _ t) = tail t
515         tail (ZLast _)   = False
516
517 -- Lower each safe call in the block, update the CAF and slot environments
518 -- to include each of those calls, and insert the new block in the blockEnv.
519 lowerSafeCallBlock :: SafeState-> CmmBlock -> FuelMonad SafeState
520 lowerSafeCallBlock state b = tail (return state) (ZBlock head (ZLast last))
521   where (head, last) = goto_end (G.unzip b)
522         tail s b@(ZBlock (ZFirst _ _) _) =
523           do state <- s
524              return $ state { s_blocks = insertBlock (G.zip b) (s_blocks state) }
525         tail  s (ZBlock (ZHead h m@(MidForeignCall (Safe bid updfr_off) _ _ _)) t) =
526           do state <- s
527              let state' = state
528                    { s_safeCalls = FloatingInfoTable emptyContInfoTable bid updfr_off :
529                                      s_safeCalls state }
530              (state'', t') <- lowerSafeForeignCall state' m t
531              tail (return state'') (ZBlock h t')
532         tail s (ZBlock (ZHead h m) t) = tail s (ZBlock h (ZTail m t))
533            
534
535 -- Late in the code generator, we want to insert the code necessary
536 -- to lower a safe foreign call to a sequence of unsafe calls.
537 lowerSafeForeignCall ::
538   SafeState -> Middle -> ZTail Middle Last -> FuelMonad (SafeState, ZTail Middle Last)
539 lowerSafeForeignCall state m@(MidForeignCall (Safe infotable updfr) _ _ _) tail = do
540     let newTemp rep = getUniqueM >>= \u -> return (LocalReg u rep)
541     -- Both 'id' and 'new_base' are KindNonPtr because they're
542     -- RTS-only objects and are not subject to garbage collection
543     id <- newTemp bWord
544     new_base <- newTemp (cmmRegType (CmmGlobal BaseReg))
545     let (caller_save, caller_load) = callerSaveVolatileRegs 
546     load_tso <- newTemp gcWord -- TODO FIXME NOW
547     let suspendThread = CmmLit (CmmLabel (mkRtsCodeLabel (sLit "suspendThread")))
548         resumeThread  = CmmLit (CmmLabel (mkRtsCodeLabel (sLit "resumeThread")))
549         suspend = mkStore (CmmReg spReg) (CmmLit (CmmBlock infotable)) <*>
550                   saveThreadState <*>
551                   caller_save <*>
552                   mkUnsafeCall (ForeignTarget suspendThread
553                                   (ForeignConvention CCallConv [AddrHint] [AddrHint]))
554                     [id] [CmmReg (CmmGlobal BaseReg)]
555         resume = mkUnsafeCall (ForeignTarget resumeThread
556                                   (ForeignConvention CCallConv [AddrHint] [AddrHint]))
557                     [new_base] [CmmReg (CmmLocal id)] <*>
558                  -- Assign the result to BaseReg: we
559                  -- might now have a different Capability!
560                  mkAssign (CmmGlobal BaseReg) (CmmReg (CmmLocal new_base)) <*>
561                  caller_load <*>
562                  loadThreadState load_tso
563     Graph tail' blocks' <-
564       liftUniq (graphOfAGraph (suspend <*> mkMiddle m <*> resume <*> mkZTail tail))
565     return (state {s_blocks = s_blocks state `plusBlockEnv` blocks'}, tail')
566 lowerSafeForeignCall _ _ _ = panic "lowerSafeForeignCall was passed something else"