CgBindings, CgIdInfo,
StableLoc, VolatileLoc,
- stableAmodeIdInfo, heapIdInfo, newTempAmodeAndIdInfo,
+ stableAmodeIdInfo, heapIdInfo,
letNoEscapeIdInfo, idInfoToAmode,
addBindC, addBindsC,
bindNewToStack, rebindToStack,
bindNewToNode, bindNewToReg, bindArgsToRegs,
- bindNewToTemp, bindNewPrimToAmode,
+ bindNewToTemp,
getArgAmode, getArgAmodes,
getCAddrModeAndInfo, getCAddrMode,
getCAddrModeIfVolatile, getVolatileRegs,
- buildLivenessMask, buildContLivenessMask
+ buildContLivenessMask
) where
#include "HsVersions.h"
import CgMonad
import CgUsages ( getHpRelOffset, getSpRelOffset, getRealSp )
-import CgStackery ( freeStackSlots, addFreeSlots )
-import CLabel ( mkStaticClosureLabel, mkClosureLabel,
+import CgStackery ( freeStackSlots, getStackFrame )
+import CLabel ( mkClosureLabel,
mkBitmapLabel, pprCLabel )
import ClosureInfo ( mkLFImported, mkLFArgument, LambdaFormInfo )
-import BitSet ( mkBS, emptyBS )
+import Bitmap
import PrimRep ( isFollowableRep, getPrimRepSize )
-import DataCon ( DataCon, dataConName )
import Id ( Id, idPrimRep, idType )
import Type ( typePrimRep )
import VarEnv
import VarSet ( varSetElems )
-import Const ( Con(..), Literal )
-import Maybes ( catMaybes, maybeToBool )
-import Name ( isLocallyDefined, isWiredInName, NamedThing(..) )
-#ifdef DEBUG
-import PprAbsC ( pprAmode )
-#endif
+import Literal ( Literal )
+import Maybes ( catMaybes, maybeToBool, seqMaybe )
+import Name ( isInternalName, NamedThing(..) )
+import PprAbsC ( pprAmode, pprMagicId )
import PrimRep ( PrimRep(..) )
-import StgSyn ( StgArg, StgLiveVars, GenStgArg(..) )
+import StgSyn ( StgArg, StgLiveVars, GenStgArg(..), isStgTypeArg )
import Unique ( Unique, Uniquable(..) )
import UniqSet ( elementOfUniqSet )
import Util ( zipWithEqual, sortLt )
| TempVarLoc Unique
| RegLoc MagicId -- in one of the magic registers
- -- (probably {Int,Float,Char,etc}Reg
+ -- (probably {Int,Float,Char,etc}Reg)
| VirHpLoc VirtualHeapOffset -- Hp+offset (address of closure)
maybeStkLoc _ = Nothing
\end{code}
+\begin{code}
+instance Outputable CgIdInfo where
+ ppr (MkCgIdInfo id vol stb lf)
+ = ppr id <+> ptext SLIT("-->") <+> vcat [ppr vol, ppr stb]
+
+instance Outputable VolatileLoc where
+ ppr NoVolatileLoc = empty
+ ppr (TempVarLoc u) = ptext SLIT("tmp") <+> ppr u
+ ppr (RegLoc r) = ptext SLIT("reg") <+> pprMagicId r
+ ppr (VirHpLoc v) = ptext SLIT("vh") <+> ppr v
+ ppr (VirNodeLoc v) = ptext SLIT("vn") <+> ppr v
+
+instance Outputable StableLoc where
+ ppr NoStableLoc = empty
+ ppr (VirStkLoc v) = ptext SLIT("vs") <+> ppr v
+ ppr (LitLoc l) = ptext SLIT("lit") <+> ppr l
+ ppr (StableAmodeLoc a) = ptext SLIT("amode") <+> pprAmode a
+\end{code}
+
%************************************************************************
%* *
\subsection[Bindery-idInfo]{Manipulating IdInfo}
letNoEscapeIdInfo i sp lf_info
= MkCgIdInfo i NoVolatileLoc (StableAmodeLoc (CJoinPoint sp)) lf_info
-newTempAmodeAndIdInfo :: Id -> LambdaFormInfo -> (CAddrMode, CgIdInfo)
-
-newTempAmodeAndIdInfo name lf_info
- = (temp_amode, temp_idinfo)
- where
- uniq = getUnique name
- temp_amode = CTemp uniq (idPrimRep name)
- temp_idinfo = tempIdInfo name uniq lf_info
-
idInfoToAmode :: PrimRep -> CgIdInfo -> FCode CAddrMode
idInfoToAmode kind (MkCgIdInfo _ vol stab _) = idInfoPiecesToAmode kind vol stab
\begin{code}
addBindC :: Id -> CgIdInfo -> Code
-addBindC name stuff_to_bind info_down (MkCgState absC binds usage)
- = MkCgState absC (extendVarEnv binds name stuff_to_bind) usage
+addBindC name stuff_to_bind = do
+ binds <- getBinds
+ setBinds $ extendVarEnv binds name stuff_to_bind
addBindsC :: [(Id, CgIdInfo)] -> Code
-addBindsC new_bindings info_down (MkCgState absC binds usage)
- = MkCgState absC new_binds usage
- where
- new_binds = foldl (\ binds (name,info) -> extendVarEnv binds name info)
- binds
- new_bindings
+addBindsC new_bindings = do
+ binds <- getBinds
+ let new_binds = foldl (\ binds (name,info) -> extendVarEnv binds name info)
+ binds
+ new_bindings
+ setBinds new_binds
modifyBindC :: Id -> (CgIdInfo -> CgIdInfo) -> Code
-modifyBindC name mangle_fn info_down (MkCgState absC binds usage)
- = MkCgState absC (modifyVarEnv mangle_fn binds name) usage
+modifyBindC name mangle_fn = do
+ binds <- getBinds
+ setBinds $ modifyVarEnv mangle_fn binds name
lookupBindC :: Id -> FCode CgIdInfo
-lookupBindC name info_down@(MkCgInfoDown _ static_binds srt ticky _)
- state@(MkCgState absC local_binds usage)
- = (val, state)
- where
- val = case (lookupVarEnv local_binds name) of
- Nothing -> try_static
- Just this -> this
-
- try_static =
- case (lookupVarEnv static_binds name) of
- Just this -> this
- Nothing
- -> cgPanic (text "lookupBindC: no info for" <+> ppr name) info_down state
-
-cgPanic :: SDoc -> CgInfoDownwards -> CgState -> a
-cgPanic doc info_down@(MkCgInfoDown _ static_binds srt ticky _)
- state@(MkCgState absC local_binds usage)
- = pprPanic "cgPanic"
- (vcat [doc,
+lookupBindC id = do maybe_info <- lookupBindC_maybe id
+ case maybe_info of
+ Just info -> return info
+ Nothing -> cgLookupPanic id
+
+lookupBindC_maybe :: Id -> FCode (Maybe CgIdInfo)
+lookupBindC_maybe id
+ = do static_binds <- getStaticBinds
+ local_binds <- getBinds
+ return (lookupVarEnv local_binds id
+ `seqMaybe`
+ lookupVarEnv static_binds id)
+
+cgLookupPanic :: Id -> FCode a
+cgLookupPanic id
+ = do static_binds <- getStaticBinds
+ local_binds <- getBinds
+ srt <- getSRTLabel
+ pprPanic "cgPanic"
+ (vcat [ppr id,
ptext SLIT("static binds for:"),
vcat [ ppr i | (MkCgIdInfo i _ _ _) <- rngVarEnv static_binds ],
ptext SLIT("local binds for:"),
I {\em think} all looking-up is done through @getCAddrMode(s)@.
\begin{code}
-getCAddrModeAndInfo :: Id -> FCode (CAddrMode, LambdaFormInfo)
+getCAddrModeAndInfo :: Id -> FCode (Id, CAddrMode, LambdaFormInfo)
getCAddrModeAndInfo id
- | not (isLocallyDefined name) || isWiredInName name
- {- Why the "isWiredInName"?
- Imagine you are compiling PrelBase.hs (a module that
- supplies some of the wired-in values). What can
- happen is that the compiler will inject calls to
- (e.g.) GHCbase.unpackPS, where-ever it likes -- it
- assumes those values are ubiquitously available.
- The main point is: it may inject calls to them earlier
- in GHCbase.hs than the actual definition...
- -}
- = returnFC (global_amode, mkLFImported id)
-
- | otherwise = -- *might* be a nested defn: in any case, it's something whose
- -- definition we will know about...
- lookupBindC id `thenFC` \ (MkCgIdInfo _ volatile_loc stable_loc lf_info) ->
- idInfoPiecesToAmode kind volatile_loc stable_loc `thenFC` \ amode ->
- returnFC (amode, lf_info)
+ = do
+ maybe_cg_id_info <- lookupBindC_maybe id
+ case maybe_cg_id_info of
+
+ -- Nothing => not in the environment, so should be imported
+ Nothing | isInternalName name -> cgLookupPanic id
+ | otherwise -> returnFC (id, global_amode, mkLFImported id)
+
+ Just (MkCgIdInfo id' volatile_loc stable_loc lf_info)
+ -> do amode <- idInfoPiecesToAmode kind volatile_loc stable_loc
+ return (id', amode, lf_info)
where
name = getName id
global_amode = CLbl (mkClosureLabel name) kind
kind = idPrimRep id
getCAddrMode :: Id -> FCode CAddrMode
-getCAddrMode name
- = getCAddrModeAndInfo name `thenFC` \ (amode, _) ->
- returnFC amode
+getCAddrMode name = do
+ (_, amode, _) <- getCAddrModeAndInfo name
+ return amode
\end{code}
\begin{code}
getCAddrModeIfVolatile name
-- | toplevelishId name = returnFC Nothing
-- | otherwise
- = lookupBindC name `thenFC` \ ~(MkCgIdInfo _ volatile_loc stable_loc lf_info) ->
- case stable_loc of
- NoStableLoc -> -- Aha! So it is volatile!
- idInfoPiecesToAmode (idPrimRep name) volatile_loc NoStableLoc `thenFC` \ amode ->
- returnFC (Just amode)
-
- a_stable_loc -> returnFC Nothing
+ = do
+ (MkCgIdInfo _ volatile_loc stable_loc lf_info) <- lookupBindC name
+ case stable_loc of
+ NoStableLoc -> do -- Aha! So it is volatile!
+ amode <- idInfoPiecesToAmode (idPrimRep name) volatile_loc NoStableLoc
+ return $ Just amode
+ a_stable_loc -> return Nothing
\end{code}
@getVolatileRegs@ gets a set of live variables, and returns a list of
\begin{code}
getVolatileRegs :: StgLiveVars -> FCode [MagicId]
-getVolatileRegs vars
- = mapFCs snaffle_it (varSetElems vars) `thenFC` \ stuff ->
- returnFC (catMaybes stuff)
- where
- snaffle_it var
- = lookupBindC var `thenFC` \ (MkCgIdInfo _ volatile_loc stable_loc lf_info) ->
- let
- -- commoned-up code...
- consider_reg reg
- = if not (isVolatileReg reg) then
- -- Potentially dies across C calls
- -- For now, that's everything; we leave
- -- it to the save-macros to decide which
- -- regs *really* need to be saved.
- returnFC Nothing
- else
- case stable_loc of
- NoStableLoc -> returnFC (Just reg) -- got one!
- is_a_stable_loc ->
- -- has both volatile & stable locations;
- -- force it to rely on the stable location
- modifyBindC var nuke_vol_bind `thenC`
- returnFC Nothing
- in
- case volatile_loc of
- RegLoc reg -> consider_reg reg
- VirHpLoc _ -> consider_reg Hp
- VirNodeLoc _ -> consider_reg node
- non_reg_loc -> returnFC Nothing
-
- nuke_vol_bind (MkCgIdInfo i _ stable_loc lf_info)
- = MkCgIdInfo i NoVolatileLoc stable_loc lf_info
+getVolatileRegs vars = do
+ stuff <- mapFCs snaffle_it (varSetElems vars)
+ returnFC $ catMaybes stuff
+ where
+ snaffle_it var = do
+ (MkCgIdInfo _ volatile_loc stable_loc lf_info) <- lookupBindC var
+ let
+ -- commoned-up code...
+ consider_reg reg =
+ if not (isVolatileReg reg) then
+ -- Potentially dies across C calls
+ -- For now, that's everything; we leave
+ -- it to the save-macros to decide which
+ -- regs *really* need to be saved.
+ returnFC Nothing
+ else
+ case stable_loc of
+ NoStableLoc -> returnFC (Just reg) -- got one!
+ is_a_stable_loc -> do
+ -- has both volatile & stable locations;
+ -- force it to rely on the stable location
+ modifyBindC var nuke_vol_bind
+ return Nothing
+ in
+ case volatile_loc of
+ RegLoc reg -> consider_reg reg
+ VirNodeLoc _ -> consider_reg node
+ non_reg_loc -> returnFC Nothing
+
+ nuke_vol_bind (MkCgIdInfo i _ stable_loc lf_info)
+ = MkCgIdInfo i NoVolatileLoc stable_loc lf_info
\end{code}
\begin{code}
getArgAmodes :: [StgArg] -> FCode [CAddrMode]
getArgAmodes [] = returnFC []
getArgAmodes (atom:atoms)
- = getArgAmode atom `thenFC` \ amode ->
- getArgAmodes atoms `thenFC` \ amodes ->
- returnFC ( amode : amodes )
+ | isStgTypeArg atom
+ = getArgAmodes atoms
+ | otherwise = do
+ amode <- getArgAmode atom
+ amodes <- getArgAmodes atoms
+ return ( amode : amodes )
getArgAmode :: StgArg -> FCode CAddrMode
getArgAmode (StgVarArg var) = getCAddrMode var -- The common case
-
-getArgAmode (StgConArg (DataCon con))
- {- Why does this case differ from StgVarArg?
- Because the program might look like this:
- data Foo a = Empty | Baz a
- f a x = let c = Empty! a
- in h c
- Now, when we go Core->Stg, we drop the type applications,
- so we can inline c, giving
- f x = h Empty
- Now we are referring to Empty as an argument (rather than in an STGCon),
- so we'll look it up with getCAddrMode. We want to return an amode for
- the static closure that we make for nullary constructors. But if we blindly
- go ahead with getCAddrMode we end up looking in the environment, and it ain't there!
-
- This special case used to be in getCAddrModeAndInfo, but it doesn't work there.
- Consider:
- f a x = Baz a x
- If the constructor Baz isn't inlined we simply want to treat it like any other
- identifier, with a top level definition. We don't want to spot that it's a constructor.
-
- In short
- StgApp con args
- and
- StgCon con args
- are treated differently; the former is a call to a bog standard function while the
- latter uses the specially-labelled, pre-defined info tables etc for the constructor.
-
- The way to think of this case in getArgAmode is that
- SApp f Empty
- is really
- App f (StgCon Empty [])
- -}
- = returnFC (CLbl (mkStaticClosureLabel (dataConName con)) PtrRep)
-
-
-getArgAmode (StgConArg (Literal lit)) = returnFC (CLit lit)
+getArgAmode (StgLitArg lit) = returnFC (CLit lit)
\end{code}
%************************************************************************
bindNewToStack (name, offset)
= addBindC name info
where
- info = MkCgIdInfo name NoVolatileLoc (VirStkLoc offset) mkLFArgument
+ info = MkCgIdInfo name NoVolatileLoc (VirStkLoc offset) (mkLFArgument name)
bindNewToNode :: Id -> VirtualHeapOffset -> LambdaFormInfo -> Code
bindNewToNode name offset lf_info
-- bind the id to it, and return the addressing mode for the
-- temporary.
bindNewToTemp :: Id -> FCode CAddrMode
-bindNewToTemp name
- = let (temp_amode, id_info) = newTempAmodeAndIdInfo name mkLFArgument
- -- This is used only for things we don't know
- -- anything about; values returned by a case statement,
- -- for example.
- in
- addBindC name id_info `thenC`
- returnFC temp_amode
+bindNewToTemp id
+ = do addBindC id id_info
+ return temp_amode
+ where
+ uniq = getUnique id
+ temp_amode = CTemp uniq (idPrimRep id)
+ id_info = tempIdInfo id uniq lf_info
+ lf_info = mkLFArgument id -- Always used of things we
+ -- know nothing about
bindNewToReg :: Id -> MagicId -> LambdaFormInfo -> Code
bindNewToReg name magic_id lf_info
where
info = MkCgIdInfo name (RegLoc magic_id) NoStableLoc lf_info
-bindNewToLit name lit
- = addBindC name info
- where
- info = MkCgIdInfo name NoVolatileLoc (LitLoc lit) (error "bindNewToLit")
-
bindArgsToRegs :: [Id] -> [MagicId] -> Code
bindArgsToRegs args regs
= listCs (zipWithEqual "bindArgsToRegs" bind args regs)
where
- arg `bind` reg = bindNewToReg arg reg mkLFArgument
-\end{code}
-
-@bindNewPrimToAmode@ works only for certain addressing modes. Making
-this work for stack offsets is non-trivial (virt vs. real stack offset
-difficulties).
-
-\begin{code}
-bindNewPrimToAmode :: Id -> CAddrMode -> Code
-bindNewPrimToAmode name (CReg reg)
- = bindNewToReg name reg (panic "bindNewPrimToAmode")
-
-bindNewPrimToAmode name (CTemp uniq kind)
- = addBindC name (tempIdInfo name uniq (panic "bindNewPrimToAmode"))
-
-#ifdef DEBUG
-bindNewPrimToAmode name amode
- = pprPanic "bindNew...:" (pprAmode amode)
-#endif
+ arg `bind` reg = bindNewToReg arg reg (mkLFArgument arg)
\end{code}
\begin{code}
%* *
%************************************************************************
-ToDo: remove the dependency on 32-bit words.
-
There are four kinds of things on the stack:
- pointer variables (bound in the environment)
- free slots (recorded in the stack free list)
- non-pointer data slots (recorded in the stack free list)
-We build up a bitmap of non-pointer slots by looking down the
-environment for all the non-pointer variables, and merging this with
-the slots recorded in the stack free list.
-
-There's a bit of a hack here to do with update frames: since nothing
-is recorded in either the environment or the stack free list for an
-update frame, the code below defaults to assuming the slots taken up
-by an update frame contain pointers. Furthermore, update frames are
-always in slots 0-2 at the bottom of the stack. The bitmap will
-therefore end at slot 3, which is what we want (the update frame info
-pointer has its own bitmap to describe the update frame).
+We build up a bitmap of non-pointer slots by searching the environment
+for all the pointer variables, and subtracting these from a bitmap
+with initially all bits set (up to the size of the stack frame).
\begin{code}
buildLivenessMask
- :: Unique -- unique for for large bitmap label
+ :: VirtualSpOffset -- size of the stack frame
-> VirtualSpOffset -- offset from which the bitmap should start
- -> FCode Liveness -- mask for free/unlifted slots
-
-buildLivenessMask uniq sp info_down
- state@(MkCgState abs_c binds ((vsp, free, _, _), heap_usage))
- = ASSERT(all (>=0) rel_slots)
- livenessToAbsC uniq liveness_mask info_down state
- where
- -- find all unboxed stack-resident ids
- unboxed_slots =
- [ (ofs, size) |
- (MkCgIdInfo id _ (VirStkLoc ofs) _) <- rngVarEnv binds,
- let rep = idPrimRep id; size = getPrimRepSize rep,
- not (isFollowableRep rep),
- size > 0
- ]
-
- -- flatten this list into a list of unboxed stack slots
- flatten_slots = sortLt (<)
- (foldr (\(ofs,size) r -> [ofs-size+1 .. ofs] ++ r) []
- unboxed_slots)
-
- -- merge in the free slots
- all_slots = mergeSlots flatten_slots (map fst free) ++
- if vsp < sp then [vsp+1 .. sp] else []
-
- -- recalibrate the list to be sp-relative
- rel_slots = reverse (map (sp-) all_slots)
-
- -- build the bitmap
- liveness_mask = listToLivenessMask rel_slots
-
-mergeSlots :: [Int] -> [Int] -> [Int]
-mergeSlots cs [] = cs
-mergeSlots [] ns = ns
-mergeSlots (c:cs) (n:ns)
- = if c < n then
- c : mergeSlots cs (n:ns)
- else if c > n then
- n : mergeSlots (c:cs) ns
- else
- panic ("mergeSlots: equal slots: " ++ show (c:cs) ++ show (n:ns))
-
-listToLivenessMask :: [Int] -> LivenessMask
-listToLivenessMask [] = []
-listToLivenessMask slots =
- mkBS this : listToLivenessMask (map (\x -> x-32) rest)
- where (this,rest) = span (<32) slots
-
-livenessToAbsC :: Unique -> LivenessMask -> FCode Liveness
-livenessToAbsC uniq [] = returnFC (LvSmall emptyBS)
-livenessToAbsC uniq [one] = returnFC (LvSmall one)
-livenessToAbsC uniq many =
- absC (CBitmap lbl many) `thenC`
- returnFC (LvLarge lbl)
- where lbl = mkBitmapLabel uniq
-\end{code}
-
-In a continuation, we want a liveness mask that starts from just after
-the return address, which is on the stack at realSp.
-
-\begin{code}
-buildContLivenessMask
- :: Unique
- -> FCode Liveness
-buildContLivenessMask uniq
- = getRealSp `thenFC` \ realSp ->
- buildLivenessMask uniq (realSp-1)
+ -> FCode Bitmap -- mask for free/unlifted slots
+
+buildLivenessMask size sp = do {
+ -- find all live stack-resident pointers
+ binds <- getBinds;
+ ((vsp, _, free, _, _), heap_usage) <- getUsage;
+
+ let {
+ rel_slots = sortLt (<)
+ [ sp - ofs -- get slots relative to top of frame
+ | (MkCgIdInfo id _ (VirStkLoc ofs) _) <- rngVarEnv binds,
+ isFollowableRep (idPrimRep id)
+ ];
+ };
+
+ WARN( not (all (>=0) rel_slots), ppr size $$ ppr sp $$ ppr rel_slots $$ ppr binds )
+ return (intsToReverseBitmap size rel_slots)
+ }
+
+-- In a continuation, we want a liveness mask that starts from just after
+-- the return address, which is on the stack at realSp.
+
+buildContLivenessMask :: Id -> FCode Liveness
+ -- The Id is used just for its unique to make a label
+buildContLivenessMask id = do
+ realSp <- getRealSp
+
+ frame_sp <- getStackFrame
+ -- realSp points to the frame-header for the current stack frame,
+ -- and the end of this frame is frame_sp. The size is therefore
+ -- realSp - frame_sp - 1 (subtract one for the frame-header).
+ let frame_size = realSp - frame_sp - 1
+
+ mask <- buildLivenessMask frame_size (realSp-1)
+
+ let liveness = Liveness (mkBitmapLabel (getName id)) frame_size mask
+ absC (maybeLargeBitmap liveness)
+ return liveness
\end{code}
%************************************************************************
\begin{code}
nukeDeadBindings :: StgLiveVars -- All the *live* variables
-> Code
-
-nukeDeadBindings live_vars info_down (MkCgState abs_c binds usage)
- = freeStackSlots extra_free info_down (MkCgState abs_c (mkVarEnv bs') usage)
- where
- (dead_stk_slots, bs')
- = dead_slots live_vars
- [] []
- [ (i, b) | b@(MkCgIdInfo i _ _ _) <- rngVarEnv binds ]
-
- extra_free = sortLt (<) dead_stk_slots
+nukeDeadBindings live_vars = do
+ binds <- getBinds
+ let (dead_stk_slots, bs') =
+ dead_slots live_vars
+ [] []
+ [ (i, b) | b@(MkCgIdInfo i _ _ _) <- rngVarEnv binds ]
+ setBinds $ mkVarEnv bs'
+ freeStackSlots dead_stk_slots
\end{code}
Several boring auxiliary functions to do the dirty work.