X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=compiler%2Fcmm%2FZipCfgCmmRep.hs;h=aa16f0b1988322f1685b88610a411ef7973db71a;hp=05203e54d2a081a8cab3b04aa51d28e099daa6c0;hb=83d563cb9ede0ba792836e529b1e2929db926355;hpb=e6243a818496aad82b6f47511d3bd9bc800f747d diff --git a/compiler/cmm/ZipCfgCmmRep.hs b/compiler/cmm/ZipCfgCmmRep.hs index 05203e5..aa16f0b 100644 --- a/compiler/cmm/ZipCfgCmmRep.hs +++ b/compiler/cmm/ZipCfgCmmRep.hs @@ -1,3 +1,8 @@ +#if __GLASGOW_HASKELL__ >= 611 +{-# OPTIONS_GHC -XNoMonoLocalBinds #-} +#endif +-- Norman likes local bindings + -- This module is pure representation and should be imported only by -- clients that need to manipulate representation and know what -- they're doing. Clients that need to create flow graphs should @@ -29,17 +34,16 @@ import CmmTx import CLabel import FastString import ForeignCall -import qualified ZipCfg as Z import qualified ZipDataflow as DF import ZipCfg import MkZipCfg import Util +import BasicTypes import Maybes -import Monad +import Control.Monad import Outputable import Prelude hiding (zip, unzip, last) -import qualified Data.List as L import SMRep (ByteOff) import UniqSupply @@ -49,8 +53,10 @@ import UniqSupply type CmmGraph = LGraph Middle Last type CmmAGraph = AGraph Middle Last type CmmBlock = Block Middle Last -type CmmZ = GenCmm CmmStatic CmmInfo CmmGraph -type CmmTopZ = GenCmmTop CmmStatic CmmInfo CmmGraph +type CmmStackInfo = (ByteOff, Maybe ByteOff) + -- probably want a record; (SP offset on entry, update frame space) +type CmmZ = GenCmm CmmStatic CmmInfo (CmmStackInfo, CmmGraph) +type CmmTopZ = GenCmmTop CmmStatic CmmInfo (CmmStackInfo, CmmGraph) type CmmBackwardFixedPoint a = DF.BackwardFixedPoint Middle Last a () type CmmForwardFixedPoint a = DF.ForwardFixedPoint Middle Last a () @@ -64,7 +70,7 @@ data Middle | MidStore CmmExpr CmmExpr -- Assign to memory location. Size is -- given by cmmExprType of the rhs. - | MidForeignCall -- A foreign call; + | MidForeignCall -- A foreign call; see Note [Foreign calls] ForeignSafety -- Is it a safe or unsafe call? MidCallTarget -- call target and convention CmmFormals -- zero or more results @@ -84,37 +90,61 @@ data Last -- one -> second block etc -- Undefined outside range, and when there's a Nothing | LastCall { -- A call (native or safe foreign) - cml_target :: CmmExpr, -- never a CmmPrim to a CallishMachOp! - cml_cont :: Maybe BlockId, + cml_target :: CmmExpr, -- never a CmmPrim to a CallishMachOp! + + cml_cont :: Maybe BlockId, -- BlockId of continuation (Nothing for return or tail call) - cml_args :: ByteOff, -- bytes offset for youngest outgoing arg - cml_ret_off :: Maybe UpdFrameOffset} - -- stack offset for return (update frames); - -- The return offset should be Nothing only if we have to create - -- a new call, e.g. for a procpoint, in which case it's an invariant - -- that the call does not stand for a return or a tail call, - -- and the successor does not need an info table. - -data MidCallTarget -- The target of a MidUnsafeCall - = ForeignTarget -- A foreign procedure - CmmExpr -- Its address - ForeignConvention -- Its calling convention - - | PrimTarget -- A possibly-side-effecting machine operation - CallishMachOp -- Which one + + cml_args :: ByteOff, + -- Byte offset, from the *old* end of the Area associated with + -- the BlockId (if cml_cont = Nothing, then Old area), of + -- youngest outgoing arg. Set the stack pointer to this before + -- transferring control. + -- (NB: an update frame might also have been stored in the Old + -- area, but it'll be in an older part than the args.) + + cml_ret_args :: ByteOff, + -- For calls *only*, the byte offset for youngest returned value + -- This is really needed at the *return* point rather than here + -- at the call, but in practice it's convenient to record it here. + + cml_ret_off :: Maybe ByteOff + -- For calls *only*, the byte offset of the base of the frame that + -- must be described by the info table for the return point. + -- The older words are an update frames, which have their own + -- info-table and layout information + + -- From a liveness point of view, the stack words older than + -- cml_ret_off are treated as live, even if the sequel of + -- the call goes into a loop. + } + +data MidCallTarget -- The target of a MidUnsafeCall + = ForeignTarget -- A foreign procedure + CmmExpr -- Its address + ForeignConvention -- Its calling convention + + | PrimTarget -- A possibly-side-effecting machine operation + CallishMachOp -- Which one deriving Eq data Convention - = Native -- Native C-- call/return + = NativeDirectCall -- Native C-- call skipping the node (closure) argument + + | NativeNodeCall -- Native C-- call including the node argument + + | NativeReturn -- Native C-- return + + | Slow -- Slow entry points: all args pushed on the stack - | Slow -- Slow entry points: all args pushed on the stack + | GC -- Entry to the garbage collector: uses the node reg! - | GC -- Entry to the garbage collector: uses the node reg! + | PrimOpCall -- Calling prim ops - | PrimOp -- Calling prim ops + | PrimOpReturn -- Returning from prim ops - | Foreign -- Foreign call/return - ForeignConvention + | Foreign -- Foreign call/return + ForeignConvention | Private -- Used for control transfers within a (pre-CPS) procedure All @@ -135,12 +165,40 @@ data ForeignSafety = Unsafe -- unsafe call | Safe BlockId -- making infotable requires: 1. label UpdFrameOffset -- 2. where the upd frame is + Bool -- is the call interruptible? deriving Eq data ValueDirection = Arguments | Results -- Arguments go with procedure definitions, jumps, and arguments to calls -- Results go with returns and with results of calls. deriving Eq + +{- Note [Foreign calls] +~~~~~~~~~~~~~~~~~~~~~~~ +A MidForeign call is used *all* foreign calls, both *unsafe* and *safe*. +Unsafe ones are easy: think of them as a "fat machine instruction". + +Safe ones are trickier. A safe foreign call + r = f(x) +ultimately expands to + push "return address" -- Never used to return to; + -- just points an info table + save registers into TSO + call suspendThread + r = f(x) -- Make the call + call resumeThread + restore registers + pop "return address" +We cannot "lower" a safe foreign call to this sequence of Cmms, because +after we've saved Sp all the Cmm optimiser's assumptions are broken. +Furthermore, currently the smart Cmm constructors know the calling +conventions for Haskell, the garbage collector, etc, and "lower" them +so that a LastCall passes no parameters or results. But the smart +constructors do *not* (currently) know the foreign call conventions. + +For these reasons use MidForeignCall for all calls. The only annoying thing +is that a safe foreign call needs an info table. +-} ---------------------------------------------------------------------- ----- Splicing between blocks @@ -174,7 +232,7 @@ insertBetween b ms succId = insert $ goto_end $ unzip b panic "unimp: insertBetween after a call -- probably not a good idea" insert (_, LastExit) = panic "cannot insert after exit" newBlocks = do id <- liftM BlockId $ getUniqueM - return $ (id, [Block id emptyStackInfo $ + return $ (id, [Block id $ foldr ZTail (ZLast (LastOther (LastBranch succId))) ms]) mbNewBlocks (Just k) = if k == succId then liftM lift newBlocks else return (Just k, []) @@ -196,18 +254,18 @@ instance LastNode Last where branchNodeTarget _ = panic "asked for target of non-branch" cmmSuccs :: Last -> [BlockId] -cmmSuccs (LastBranch id) = [id] -cmmSuccs (LastCall _ Nothing _ _) = [] -cmmSuccs (LastCall _ (Just id) _ _) = [id] -cmmSuccs (LastCondBranch _ t f) = [f, t] -- meets layout constraint -cmmSuccs (LastSwitch _ edges) = catMaybes edges +cmmSuccs (LastBranch id) = [id] +cmmSuccs (LastCall _ Nothing _ _ _) = [] +cmmSuccs (LastCall _ (Just id) _ _ _) = [id] +cmmSuccs (LastCondBranch _ t f) = [f, t] -- meets layout constraint +cmmSuccs (LastSwitch _ edges) = catMaybes edges fold_cmm_succs :: (BlockId -> a -> a) -> Last -> a -> a -fold_cmm_succs f (LastBranch id) z = f id z -fold_cmm_succs _ (LastCall _ Nothing _ _) z = z -fold_cmm_succs f (LastCall _ (Just id) _ _) z = f id z -fold_cmm_succs f (LastCondBranch _ te fe) z = f te (f fe z) -fold_cmm_succs f (LastSwitch _ edges) z = foldl (flip f) z $ catMaybes edges +fold_cmm_succs f (LastBranch id) z = f id z +fold_cmm_succs _ (LastCall _ Nothing _ _ _) z = z +fold_cmm_succs f (LastCall _ (Just id) _ _ _) z = f id z +fold_cmm_succs f (LastCondBranch _ te fe) z = f te (f fe z) +fold_cmm_succs f (LastSwitch _ edges) z = foldl (flip f) z $ catMaybes edges ---------------------------------------------------------------------- ----- Instance declarations for register use @@ -225,8 +283,8 @@ instance UserOfLocalRegs MidCallTarget where foldRegsUsed f z (ForeignTarget e _) = foldRegsUsed f z e instance UserOfSlots MidCallTarget where + foldSlotsUsed f z (ForeignTarget e _) = foldSlotsUsed f z e foldSlotsUsed _f z (PrimTarget _) = z - foldSlotsUsed f z (ForeignTarget e _) = foldSlotsUsed f z e instance (UserOfLocalRegs a) => UserOfLocalRegs (Maybe a) where foldRegsUsed f z (Just x) = foldRegsUsed f z x @@ -239,16 +297,16 @@ instance (UserOfSlots a) => UserOfSlots (Maybe a) where instance UserOfLocalRegs Last where foldRegsUsed f z l = last l where last (LastBranch _id) = z - last (LastCall tgt _ _ _) = foldRegsUsed f z tgt + last (LastCall tgt _ _ _ _) = foldRegsUsed f z tgt last (LastCondBranch e _ _) = foldRegsUsed f z e last (LastSwitch e _tbl) = foldRegsUsed f z e instance DefinerOfLocalRegs Middle where foldRegsDefd f z m = middle m - where middle (MidComment {}) = z - middle (MidAssign _lhs _) = fold f z _lhs - middle (MidStore _ _) = z - middle (MidForeignCall _ _ fs _) = fold f z fs + where middle (MidComment {}) = z + middle (MidAssign lhs _) = fold f z lhs + middle (MidStore _ _) = z + middle (MidForeignCall _ _ fs _) = fold f z fs fold f z m = foldRegsDefd f z m -- avoid monomorphism restriction instance DefinerOfLocalRegs Last where @@ -269,7 +327,7 @@ instance UserOfSlots Middle where instance UserOfSlots Last where foldSlotsUsed f z l = last l where last (LastBranch _id) = z - last (LastCall tgt _ _ _) = foldSlotsUsed f z tgt + last (LastCall tgt _ _ _ _) = foldSlotsUsed f z tgt last (LastCondBranch e _ _) = foldSlotsUsed f z e last (LastSwitch e _tbl) = foldSlotsUsed f z e @@ -313,13 +371,13 @@ mapExpLast :: (CmmExpr -> CmmExpr) -> Last -> Last mapExpLast _ l@(LastBranch _) = l mapExpLast exp (LastCondBranch e ti fi) = LastCondBranch (exp e) ti fi mapExpLast exp (LastSwitch e tbl) = LastSwitch (exp e) tbl -mapExpLast exp (LastCall tgt mb_id u s) = LastCall (exp tgt) mb_id u s +mapExpLast exp (LastCall tgt mb_id o i s) = LastCall (exp tgt) mb_id o i s foldExpLast :: (CmmExpr -> z -> z) -> Last -> z -> z foldExpLast _ (LastBranch _) z = z foldExpLast exp (LastCondBranch e _ _) z = exp e z foldExpLast exp (LastSwitch e _) z = exp e z -foldExpLast exp (LastCall tgt _ _ _) z = exp tgt z +foldExpLast exp (LastCall tgt _ _ _ _) z = exp tgt z mapExpMidcall :: (CmmExpr -> CmmExpr) -> MidCallTarget -> MidCallTarget mapExpMidcall exp (ForeignTarget e c) = ForeignTarget (exp e) c @@ -359,11 +417,11 @@ joinOuts lattice env l = let bot = fact_bot lattice join x y = txVal $ fact_add_to lattice x y in case l of - (LastBranch id) -> env id - (LastCall _ Nothing _ _) -> bot - (LastCall _ (Just k) _ _) -> env k - (LastCondBranch _ t f) -> join (env t) (env f) - (LastSwitch _ tbl) -> foldr join bot (map env $ catMaybes tbl) + (LastBranch id) -> env id + (LastCall _ Nothing _ _ _) -> bot + (LastCall _ (Just k) _ _ _) -> env k + (LastCondBranch _ t f) -> join (env t) (env f) + (LastSwitch _ tbl) -> foldr join bot (map env $ catMaybes tbl) ---------------------------------------------------------------------- ----- Instance declarations for prettyprinting (avoids recursive imports) @@ -407,10 +465,9 @@ pprMiddle stmt = pp_stmt <+> pp_debug -- call "ccall" foo(x, y)[r1, r2]; -- ToDo ppr volatile MidForeignCall safety target results args -> - hsep [ if null results - then empty - else parens (commafy $ map ppr results) <+> equals, - ppr_safety safety, + hsep [ ppUnless (null results) $ + parens (commafy $ map ppr results) <+> equals, + ppr_safety safety, ptext $ sLit "call", ppr_call_target target <> parens (commafy $ map ppr args) <> semi] @@ -424,15 +481,23 @@ pprMiddle stmt = pp_stmt <+> pp_debug MidForeignCall {} -> text "MidForeignCall" ppr_fc :: ForeignConvention -> SDoc -ppr_fc (ForeignConvention c _ _) = doubleQuotes (ppr c) +ppr_fc (ForeignConvention c args res) = + doubleQuotes (ppr c) <+> text "args: " <+> ppr args <+> text " results: " <+> ppr res ppr_safety :: ForeignSafety -> SDoc -ppr_safety (Safe bid upd) = text "safe<" <> ppr bid <> text ", " <> ppr upd <> text ">" +ppr_safety (Safe bid upd interruptible) = + text (if interruptible then "interruptible" else "safe") <> + text "<" <> ppr bid <> text ", " <> ppr upd <> text ">" ppr_safety Unsafe = text "unsafe" ppr_call_target :: MidCallTarget -> SDoc ppr_call_target (ForeignTarget fn c) = ppr_fc c <+> ppr_target fn -ppr_call_target (PrimTarget op) = ppr (CmmLabel (mkForeignLabel (mkFastString (show op)) Nothing False)) +ppr_call_target (PrimTarget op) + -- HACK: We're just using a ForeignLabel to get this printed, the label + -- might not really be foreign. + = ppr (CmmLabel (mkForeignLabel + (mkFastString (show op)) + Nothing ForeignLabelInThisPackage IsFunction)) ppr_target :: CmmExpr -> SDoc ppr_target t@(CmmLit _) = ppr t @@ -447,10 +512,10 @@ pprLast :: Last -> SDoc pprLast stmt = pp_stmt <+> pp_debug where pp_stmt = case stmt of - LastBranch ident -> ptext (sLit "goto") <+> ppr ident <> semi - LastCondBranch expr t f -> genFullCondBranch expr t f - LastSwitch arg ids -> ppr $ CmmSwitch arg ids - LastCall tgt k off updfr_off -> genBareCall tgt k off updfr_off + LastBranch ident -> ptext (sLit "goto") <+> ppr ident <> semi + LastCondBranch expr t f -> genFullCondBranch expr t f + LastSwitch arg ids -> ppr $ CmmSwitch arg ids + LastCall tgt k out res updfr_off -> genBareCall tgt k out res updfr_off pp_debug = text " //" <+> case stmt of LastBranch {} -> text "LastBranch" @@ -458,11 +523,13 @@ pprLast stmt = pp_stmt <+> pp_debug LastSwitch {} -> text "LastSwitch" LastCall {} -> text "LastCall" -genBareCall :: CmmExpr -> Maybe BlockId -> Int -> Maybe UpdFrameOffset -> SDoc -genBareCall fn k off updfr_off = +genBareCall :: CmmExpr -> Maybe BlockId -> ByteOff -> ByteOff -> + Maybe UpdFrameOffset -> SDoc +genBareCall fn k out res updfr_off = hcat [ ptext (sLit "call"), space , pprFun fn, ptext (sLit "(...)"), space - , ptext (sLit "returns to") <+> ppr k <+> parens (ppr off) + , ptext (sLit "returns to") <+> ppr k <+> parens (ppr out) + <+> parens (ppr res) , ptext (sLit " with update frame") <+> ppr updfr_off , semi ] @@ -481,12 +548,15 @@ genFullCondBranch expr t f = ] pprConvention :: Convention -> SDoc -pprConvention (Native {}) = text "" -pprConvention Slow = text "" -pprConvention GC = text "" -pprConvention PrimOp = text "" -pprConvention (Foreign c) = ppr c -pprConvention (Private {}) = text "" +pprConvention (NativeNodeCall {}) = text "" +pprConvention (NativeDirectCall {}) = text "" +pprConvention (NativeReturn {}) = text "" +pprConvention Slow = text "" +pprConvention GC = text "" +pprConvention PrimOpCall = text "" +pprConvention PrimOpReturn = text "" +pprConvention (Foreign c) = ppr c +pprConvention (Private {}) = text "" pprForeignConvention :: ForeignConvention -> SDoc pprForeignConvention (ForeignConvention c as rs) = ppr c <> ppr as <> ppr rs