X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=compiler%2Fcmm%2FZipCfgCmmRep.hs;h=0f00641efd565df40d0ce2260914cf8f537d3480;hp=31c1fdff494a8702969232a471931f3813c09136;hb=edc0bafd3fcd01b85a2e8894e5dfe149eb0e0857;hpb=25628e2771424cae1b3366322e8ce6f8a85440f9 diff --git a/compiler/cmm/ZipCfgCmmRep.hs b/compiler/cmm/ZipCfgCmmRep.hs index 31c1fdf..0f00641 100644 --- a/compiler/cmm/ZipCfgCmmRep.hs +++ b/compiler/cmm/ZipCfgCmmRep.hs @@ -1,4 +1,5 @@ - +{-# OPTIONS_GHC -XNoMonoLocalBinds #-} +-- Norman likes local bindings -- This module is pure representation and should be imported only by -- clients that need to manipulate representation and know what @@ -6,40 +7,42 @@ -- instead import MkZipCfgCmm. module ZipCfgCmmRep - ( CmmZ, CmmTopZ, CmmGraph, CmmBlock, CmmAGraph, Middle(..), Last(..), Convention(..) - , ValueDirection(..), CmmBackwardFixedPoint, CmmForwardFixedPoint - , insertBetween, pprCmmGraphLikeCmm + ( CmmZ, CmmTopZ, CmmGraph, CmmBlock, CmmAGraph + , Middle(..), Last(..), MidCallTarget(..), UpdFrameOffset + , Convention(..), ForeignConvention(..), ForeignSafety(..) + , ValueDirection(..), ForeignHint(..) + , CmmBackwardFixedPoint, CmmForwardFixedPoint, pprHinted + , insertBetween, mapExpMiddle, mapExpLast, mapExpDeepMiddle, mapExpDeepLast + , foldExpMiddle, foldExpLast, foldExpDeepMiddle, foldExpDeepLast, joinOuts ) where +import BlockId import CmmExpr import Cmm ( GenCmm(..), GenCmmTop(..), CmmStatic, CmmInfo - , CmmCallTarget(..), CmmActuals, CmmFormals, CmmKinded(..) + , CallishMachOp(..), ForeignHint(..) + , CmmActuals, CmmFormals, CmmHinted(..) , CmmStmt(..) -- imported in order to call ppr on Switch and to -- implement pprCmmGraphLikeCmm - , CmmSafety(CmmSafe) -- for pprCmmGraphLikeCmm - , CmmReturnInfo(CmmMayReturn) -- for pprCmmGraphLikeCmm ) +import DFMonad import PprCmm() +import CmmTx import CLabel -import CmmZipUtil -import ClosureInfo import FastString import ForeignCall -import MachOp -import StackSlot -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 UniqSet +import SMRep (ByteOff) import UniqSupply ---------------------------------------------------------------------- @@ -48,47 +51,28 @@ 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 () +type UpdFrameOffset = ByteOff + data Middle = MidComment FastString | MidAssign CmmReg CmmExpr -- Assign to register | MidStore CmmExpr CmmExpr -- Assign to memory location. Size is - -- given by cmmExprRep of the rhs. + -- given by cmmExprType of the rhs. - | MidUnsafeCall -- An "unsafe" foreign call; - CmmCallTarget -- just a fat machine instruction + | 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 CmmActuals -- zero or more arguments - - | MidAddToContext -- push a frame on the stack; - -- I will return to this frame - CmmExpr -- The frame's return address; it must be - -- preceded by an info table that describes the - -- live variables. - [CmmExpr] -- The frame's live variables, to go on the - -- stack with the first one at the young end - - | CopyIn -- Move incoming parameters or results from conventional - -- locations to registers. Note [CopyIn invariant] - Convention - CmmFormals -- eventually [CmmKind] will be used only for foreign - -- calls and will migrate into 'Convention' (helping to - -- drain "the swamp"), leaving this as [LocalReg] - C_SRT -- Static things kept alive by this block - - | CopyOut Convention CmmActuals - -- Move outgoing parameters or results from registers to - -- conventional locations. Every 'LastReturn', - -- 'LastJump', or 'LastCall' must be dominated by a - -- matching 'CopyOut' in the same basic block. - -- As above, '[CmmKind]' will migrate into the foreign calling - -- convention, leaving the actuals as '[CmmExpr]'. deriving Eq data Last @@ -98,45 +82,120 @@ data Last cml_pred :: CmmExpr, cml_true, cml_false :: BlockId } - - | LastReturn -- Return from a function; values in a previous CopyOut node - - | LastJump CmmExpr -- Tail call to another procedure; args in a CopyOut node - - | LastCall { -- A call (native or safe foreign); args in CopyOut node - cml_target :: CmmExpr, -- never a CmmPrim to a CallishMachOp! - cml_cont :: Maybe BlockId } -- BlockId of continuation, if call returns - | LastSwitch CmmExpr [Maybe BlockId] -- Table branch -- The scrutinee is zero-based; -- zero -> first block -- 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, + -- BlockId of continuation (Nothing for return or tail call) + + 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 - = ConventionStandard CCallConv ValueDirection - | ConventionPrivate - -- Used for control transfers within a (pre-CPS) procedure All - -- jump sites known, never pushed on the stack (hence no SRT) - -- You can choose whatever calling convention you please - -- (provided you make sure all the call sites agree)! - -- This data type eventually to be extended to record the convention. + = 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 + + | GC -- Entry to the garbage collector: uses the node reg! + + | PrimOpCall -- Calling prim ops + + | PrimOpReturn -- Returning from prim ops + + | Foreign -- Foreign call/return + ForeignConvention + + | Private + -- Used for control transfers within a (pre-CPS) procedure All + -- jump sites known, never pushed on the stack (hence no SRT) + -- You can choose whatever calling convention you please + -- (provided you make sure all the call sites agree)! + -- This data type eventually to be extended to record the convention. + deriving( Eq ) + +data ForeignConvention + = ForeignConvention + CCallConv -- Which foreign-call convention + [ForeignHint] -- Extra info about the args + [ForeignHint] -- Extra info about the result + deriving Eq + +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 [CopyIn invariant] + +{- Note [Foreign calls] ~~~~~~~~~~~~~~~~~~~~~~~ -One might wish for CopyIn to be a First node, but in practice, the -possibility raises all sorts of hairy issues with graph splicing, -rewriting, and so on. In the end, NR finds it better to make the -placement of CopyIn a dynamic invariant; it should normally be the first -Middle node in the basic block in which it occurs. +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. -} ---------------------------------------------------------------------- @@ -151,31 +210,24 @@ Middle node in the basic block in which it occurs. -- a fresh basic block, enabling some common blockification. -- o For a conditional branch, switch statement, or call, we must insert -- a new basic block. --- o For a jump, or return, this operation is impossible. +-- o For a jump or return, this operation is impossible. insertBetween :: MonadUnique m => CmmBlock -> [Middle] -> BlockId -> m (CmmBlock, [CmmBlock]) insertBetween b ms succId = insert $ goto_end $ unzip b where insert (h, LastOther (LastBranch bid)) = if bid == succId then do (bid', bs) <- newBlocks - return (zipht h $ ZLast $ LastOther (LastBranch bid'), bs) - else panic "tried to insert between non-adjacent blocks" + return (zipht h (ZLast (LastOther (LastBranch bid'))), bs) + else panic "tried invalid block insertBetween" insert (h, LastOther (LastCondBranch c t f)) = do (t', tbs) <- if t == succId then newBlocks else return $ (t, []) (f', fbs) <- if f == succId then newBlocks else return $ (f, []) return (zipht h $ ZLast $ LastOther (LastCondBranch c t' f'), tbs ++ fbs) - insert (h, LastOther (LastCall e (Just k))) = - if k == succId then - do (id', bs) <- newBlocks - return (zipht h $ ZLast $ LastOther (LastCall e (Just id')), bs) - else panic "tried to insert between non-adjacent blocks" - insert (_, LastOther (LastCall _ Nothing)) = - panic "cannot insert after non-returning call" insert (h, LastOther (LastSwitch e ks)) = do (ids, bs) <- mapAndUnzipM mbNewBlocks ks return (zipht h $ ZLast $ LastOther (LastSwitch e ids), join bs) - insert (_, LastOther LastReturn) = panic "cannot insert after return" - insert (_, LastOther (LastJump _)) = panic "cannot insert after jump" + insert (_, LastOther (LastCall {})) = + 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 $ @@ -185,7 +237,6 @@ insertBetween b ms succId = insert $ goto_end $ unzip b mbNewBlocks Nothing = return (Nothing, []) lift (id, bs) = (Just id, bs) - ---------------------------------------------------------------------- ----- Instance declarations for control flow @@ -201,65 +252,174 @@ instance LastNode Last where branchNodeTarget _ = panic "asked for target of non-branch" cmmSuccs :: Last -> [BlockId] -cmmSuccs (LastReturn {}) = [] -cmmSuccs (LastJump {}) = [] -cmmSuccs (LastBranch id) = [id] -cmmSuccs (LastCall _ (Just id)) = [id] -cmmSuccs (LastCall _ Nothing) = [] -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 (LastReturn {}) z = z -fold_cmm_succs _f (LastJump {}) z = z -fold_cmm_succs f (LastBranch id) z = f id z -fold_cmm_succs f (LastCall _ (Just id)) z = f id z -fold_cmm_succs _f (LastCall _ Nothing) z = 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 instance UserOfLocalRegs Middle where foldRegsUsed f z m = middle m - where middle (MidComment {}) = z - middle (MidAssign _lhs expr) = fold f z expr - middle (MidStore addr rval) = fold f (fold f z addr) rval - middle (MidUnsafeCall tgt _ress args) = fold f (fold f z tgt) args - middle (MidAddToContext ra args) = fold f (fold f z ra) args - middle (CopyIn _ _formals _) = z - middle (CopyOut _ actuals) = fold f z actuals + where middle (MidComment {}) = z + middle (MidAssign _lhs expr) = fold f z expr + middle (MidStore addr rval) = fold f (fold f z addr) rval + middle (MidForeignCall _ tgt _ args) = fold f (fold f z tgt) args fold f z m = foldRegsUsed f z m -- avoid monomorphism restriction +instance UserOfLocalRegs MidCallTarget where + foldRegsUsed _f z (PrimTarget _) = z + 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 + +instance (UserOfLocalRegs a) => UserOfLocalRegs (Maybe a) where + foldRegsUsed f z (Just x) = foldRegsUsed f z x + foldRegsUsed _ z Nothing = z + +instance (UserOfSlots a) => UserOfSlots (Maybe a) where + foldSlotsUsed f z (Just x) = foldSlotsUsed f z x + foldSlotsUsed _ z Nothing = z + instance UserOfLocalRegs Last where foldRegsUsed f z l = last l - where last (LastReturn) = z - last (LastJump e) = foldRegsUsed f z e - last (LastBranch _id) = z - last (LastCall tgt _) = foldRegsUsed f z tgt + where last (LastBranch _id) = z + 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 (MidUnsafeCall _ _ _) = z - middle (MidAddToContext _ _) = z - middle (CopyIn _ _formals _) = fold f z _formals - middle (CopyOut _ _) = z + 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 - foldRegsDefd _ z l = last l - where last (LastReturn) = z - last (LastJump _) = z - last (LastBranch _) = z - last (LastCall _ _) = z - last (LastCondBranch _ _ _) = z - last (LastSwitch _ _) = z + foldRegsDefd _ z _ = z + + +---------------------------------------------------------------------- +----- Instance declarations for stack slot use + +instance UserOfSlots Middle where + foldSlotsUsed f z m = middle m + where middle (MidComment {}) = z + middle (MidAssign _lhs expr) = fold f z expr + middle (MidStore addr rval) = fold f (fold f z addr) rval + middle (MidForeignCall _ tgt _ress args) = fold f (fold f z tgt) args + fold f z e = foldSlotsUsed f z e -- avoid monomorphism restriction + +instance UserOfSlots Last where + foldSlotsUsed f z l = last l + where last (LastBranch _id) = z + last (LastCall tgt _ _ _ _) = foldSlotsUsed f z tgt + last (LastCondBranch e _ _) = foldSlotsUsed f z e + last (LastSwitch e _tbl) = foldSlotsUsed f z e + +instance UserOfSlots l => UserOfSlots (ZLast l) where + foldSlotsUsed f z (LastOther l) = foldSlotsUsed f z l + foldSlotsUsed _ z LastExit = z + +instance DefinerOfSlots Middle where + foldSlotsDefd f z m = middle m + where middle (MidComment {}) = z + middle (MidAssign _ _) = z + middle (MidForeignCall {}) = z + middle (MidStore (CmmStackSlot a i) e) = + f z (a, i, widthInBytes $ typeWidth $ cmmExprType e) + middle (MidStore _ _) = z + +instance DefinerOfSlots Last where + foldSlotsDefd _ z _ = z + +instance DefinerOfSlots l => DefinerOfSlots (ZLast l) where + foldSlotsDefd f z (LastOther l) = foldSlotsDefd f z l + foldSlotsDefd _ z LastExit = z + +---------------------------------------------------------------------- +----- Code for manipulating Middle and Last nodes + +mapExpMiddle :: (CmmExpr -> CmmExpr) -> Middle -> Middle +mapExpMiddle _ m@(MidComment _) = m +mapExpMiddle exp (MidAssign r e) = MidAssign r (exp e) +mapExpMiddle exp (MidStore addr e) = MidStore (exp addr) (exp e) +mapExpMiddle exp (MidForeignCall s tgt fs as) = + MidForeignCall s (mapExpMidcall exp tgt) fs (map exp as) + +foldExpMiddle :: (CmmExpr -> z -> z) -> Middle -> z -> z +foldExpMiddle _ (MidComment _) z = z +foldExpMiddle exp (MidAssign _ e) z = exp e z +foldExpMiddle exp (MidStore addr e) z = exp addr $ exp e z +foldExpMiddle exp (MidForeignCall _ tgt _ as) z = foldExpMidcall exp tgt $ foldr exp z as + +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 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 + +mapExpMidcall :: (CmmExpr -> CmmExpr) -> MidCallTarget -> MidCallTarget +mapExpMidcall exp (ForeignTarget e c) = ForeignTarget (exp e) c +mapExpMidcall _ m@(PrimTarget _) = m + +foldExpMidcall :: (CmmExpr -> z -> z) -> MidCallTarget -> z -> z +foldExpMidcall exp (ForeignTarget e _) z = exp e z +foldExpMidcall _ (PrimTarget _) z = z + +-- Take a transformer on expressions and apply it recursively. +wrapRecExp :: (CmmExpr -> CmmExpr) -> CmmExpr -> CmmExpr +wrapRecExp f (CmmMachOp op es) = f (CmmMachOp op $ map (wrapRecExp f) es) +wrapRecExp f (CmmLoad addr ty) = f (CmmLoad (wrapRecExp f addr) ty) +wrapRecExp f e = f e + +mapExpDeepMiddle :: (CmmExpr -> CmmExpr) -> Middle -> Middle +mapExpDeepLast :: (CmmExpr -> CmmExpr) -> Last -> Last +mapExpDeepMiddle f = mapExpMiddle $ wrapRecExp f +mapExpDeepLast f = mapExpLast $ wrapRecExp f + +-- Take a folder on expressions and apply it recursively. +wrapRecExpf :: (CmmExpr -> z -> z) -> CmmExpr -> z -> z +wrapRecExpf f e@(CmmMachOp _ es) z = foldr (wrapRecExpf f) (f e z) es +wrapRecExpf f e@(CmmLoad addr _) z = wrapRecExpf f addr (f e z) +wrapRecExpf f e z = f e z + +foldExpDeepMiddle :: (CmmExpr -> z -> z) -> Middle -> z -> z +foldExpDeepLast :: (CmmExpr -> z -> z) -> Last -> z -> z +foldExpDeepMiddle f = foldExpMiddle $ wrapRecExpf f +foldExpDeepLast f = foldExpLast $ wrapRecExpf f + +---------------------------------------------------------------------- +-- Compute the join of facts live out of a Last node. Useful for most backward +-- analyses. +joinOuts :: DataflowLattice a -> (BlockId -> a) -> Last -> a +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) ---------------------------------------------------------------------- ----- Instance declarations for prettyprinting (avoids recursive imports) @@ -273,6 +433,13 @@ instance Outputable Last where instance Outputable Convention where ppr = pprConvention +instance Outputable ForeignConvention where + ppr = pprForeignConvention + +instance Outputable ValueDirection where + ppr Arguments = ptext $ sLit "args" + ppr Results = ptext $ sLit "results" + instance DF.DebugNodes Middle Last debugPpr :: Bool @@ -280,103 +447,89 @@ debugPpr = debugIsOn pprMiddle :: Middle -> SDoc pprMiddle stmt = pp_stmt <+> pp_debug - where - pp_stmt = case stmt of - - CopyIn conv args _ -> - if null args then ptext (sLit "empty CopyIn") - else commafy (map pprKinded args) <+> equals <+> - ptext (sLit "foreign") <+> doubleQuotes(ppr conv) <+> ptext (sLit "...") - - CopyOut conv args -> - ptext (sLit "PreCopyOut: next, pass") <+> doubleQuotes(ppr conv) <+> - parens (commafy (map pprKinded args)) - - -- // text - MidComment s -> text "//" <+> ftext s - - -- reg = expr; - MidAssign reg expr -> ppr reg <+> equals <+> ppr expr <> semi - - -- rep[lv] = expr; - MidStore lv expr -> rep <> brackets(ppr lv) <+> equals <+> ppr expr <> semi - where - rep = ppr ( cmmExprRep expr ) - - -- call "ccall" foo(x, y)[r1, r2]; - -- ToDo ppr volatile - MidUnsafeCall (CmmCallee fn cconv) results args -> - hcat [ if null results - then empty - else parens (commafy $ map ppr results) <> - ptext (sLit " = "), - ptext (sLit "call"), space, - doubleQuotes(ppr cconv), space, - ppr_target fn, parens ( commafy $ map ppr args ), - semi ] - - MidUnsafeCall (CmmPrim op) results args -> - pprMiddle (MidUnsafeCall (CmmCallee (CmmLit lbl) CCallConv) results args) - where - lbl = CmmLabel (mkForeignLabel (mkFastString (show op)) Nothing False) - - MidAddToContext ra args -> - hcat [ ptext (sLit "return via ") - , ppr_target ra, parens (commafy $ map ppr args), semi ] - - pp_debug = - if not debugPpr then empty - else text " //" <+> - case stmt of - CopyIn {} -> text "CopyIn" - CopyOut {} -> text "CopyOut" - MidComment {} -> text "MidComment" - MidAssign {} -> text "MidAssign" - MidStore {} -> text "MidStore" - MidUnsafeCall {} -> text "MidUnsafeCall" - MidAddToContext {} -> text "MidAddToContext" - + where + pp_stmt = case stmt of + -- // text + MidComment s -> text "//" <+> ftext s + + -- reg = expr; + MidAssign reg expr -> ppr reg <+> equals <+> ppr expr <> semi + + -- rep[lv] = expr; + MidStore lv expr -> rep <> brackets(ppr lv) <+> equals <+> ppr expr <> semi + where + rep = ppr ( cmmExprType expr ) + + -- call "ccall" foo(x, y)[r1, r2]; + -- ToDo ppr volatile + MidForeignCall safety target results args -> + 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] + + pp_debug = + if not debugPpr then empty + else text " //" <+> + case stmt of + MidComment {} -> text "MidComment" + MidAssign {} -> text "MidAssign" + MidStore {} -> text "MidStore" + MidForeignCall {} -> text "MidForeignCall" + +ppr_fc :: ForeignConvention -> SDoc +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 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) + -- 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 ppr_target fn' = parens (ppr fn') - -pprKinded :: Outputable a => CmmKinded a -> SDoc -pprKinded (CmmKinded a NoHint) = ppr a -pprKinded (CmmKinded a PtrHint) = doubleQuotes (text "address") <+> ppr a -pprKinded (CmmKinded a SignedHint) = doubleQuotes (text "signed") <+> ppr a -pprKinded (CmmKinded a FloatHint) = doubleQuotes (text "float") <+> ppr a +pprHinted :: Outputable a => CmmHinted a -> SDoc +pprHinted (CmmHinted a NoHint) = ppr a +pprHinted (CmmHinted a AddrHint) = doubleQuotes (text "address") <+> ppr a +pprHinted (CmmHinted a SignedHint) = doubleQuotes (text "signed") <+> ppr a pprLast :: Last -> SDoc -pprLast stmt = (case stmt of - LastBranch ident -> ptext (sLit "goto") <+> ppr ident <> semi - LastCondBranch expr t f -> genFullCondBranch expr t f - LastJump expr -> hcat [ ptext (sLit "jump"), space, pprFun expr - , ptext (sLit "(...)"), semi] - LastReturn -> hcat [ ptext (sLit "return"), space - , ptext (sLit "(...)"), semi] - LastSwitch arg ids -> ppr $ CmmSwitch arg ids - LastCall tgt k -> genBareCall tgt k - ) <> - if debugPpr then empty - else text " //" <+> - case stmt of - LastBranch {} -> text "LastBranch" - LastCondBranch {} -> text "LastCondBranch" - LastJump {} -> text "LastJump" - LastReturn {} -> text "LastReturn" - LastSwitch {} -> text "LastSwitch" - LastCall {} -> text "LastCall" - -genBareCall :: CmmExpr -> Maybe BlockId -> SDoc -genBareCall fn k = +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 out res updfr_off -> genBareCall tgt k out res updfr_off + + pp_debug = text " //" <+> case stmt of + LastBranch {} -> text "LastBranch" + LastCondBranch {} -> text "LastCondBranch" + LastSwitch {} -> text "LastSwitch" + LastCall {} -> text "LastCall" + +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 - , case k of Nothing -> ptext (sLit "never returns") - Just k -> ptext (sLit "returns to") <+> ppr k + , ptext (sLit "returns to") <+> ppr k <+> parens (ppr out) + <+> parens (ppr res) + , ptext (sLit " with update frame") <+> ppr updfr_off , semi ] - where pprFun :: CmmExpr -> SDoc pprFun f@(CmmLit _) = ppr f @@ -393,119 +546,18 @@ genFullCondBranch expr t f = ] pprConvention :: Convention -> SDoc -pprConvention (ConventionStandard c _) = ppr c -pprConvention (ConventionPrivate {} ) = 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 commafy :: [SDoc] -> SDoc commafy xs = hsep $ punctuate comma xs - - ----------------------------------------------------------------- --- | The purpose of this function is to print a Cmm zipper graph "as if it were" --- a Cmm program. The objective is dodgy, so it's unsurprising parts of the --- code are dodgy as well. - -pprCmmGraphLikeCmm :: CmmGraph -> SDoc -pprCmmGraphLikeCmm g = vcat (swallow blocks) - where blocks = Z.postorder_dfs g - swallow :: [CmmBlock] -> [SDoc] - swallow [] = [] - swallow (Z.Block id t : rest) = tail id [] Nothing t rest - tail id prev' out (Z.ZTail (CopyOut conv args) t) rest = - if isJust out then panic "multiple CopyOut nodes in one basic block" - else - tail id (prev') (Just (conv, args)) t rest - tail id prev' out (Z.ZTail m t) rest = tail id (mid m : prev') out t rest - tail id prev' out (Z.ZLast Z.LastExit) rest = exit id prev' out rest - tail id prev' out (Z.ZLast (Z.LastOther l)) rest = last id prev' out l rest - mid (CopyIn _ [] _) = text "// proc point (no parameters)" - mid m@(CopyIn {}) = ppr m <+> text "(proc point)" - mid m = ppr m - block' id prev' - | id == Z.lg_entry g, entry_has_no_pred = - vcat (text "" : reverse prev') - | otherwise = hang (ppr id <> colon) 4 (vcat (reverse prev')) - last id prev' out l n = - let endblock stmt = block' id (stmt : prev') : swallow n in - case l of - LastBranch tgt -> - case n of - Z.Block id' t : bs - | tgt == id', unique_pred id' - -> tail id prev' out t bs -- optimize out redundant labels - _ -> endblock (ppr $ CmmBranch tgt) - l@(LastCondBranch expr tid fid) -> - let ft id = text "// fall through to " <> ppr id in - case n of - Z.Block id' t : bs - | id' == fid, isNothing out -> - tail id (ft fid : ppr (CmmCondBranch expr tid) : prev') Nothing t bs - | id' == tid, Just e' <- maybeInvertCmmExpr expr, isNothing out-> - tail id (ft tid : ppr (CmmCondBranch e' fid) : prev') Nothing t bs - _ -> endblock $ with_out out l - l@(LastJump {}) -> endblock $ with_out out l - l@(LastReturn {}) -> endblock $ with_out out l - l@(LastSwitch {}) -> endblock $ with_out out l - l@(LastCall _ Nothing) -> endblock $ with_out out l - l@(LastCall tgt (Just k)) - | Z.Block id' (Z.ZTail (CopyIn _ ress srt) t) : bs <- n, - Just (conv, args) <- out, - id' == k -> - let call = CmmCall tgt' ress args (CmmSafe srt) CmmMayReturn - tgt' = CmmCallee tgt (cconv_of_conv conv) - ppcall = ppr call <+> parens (text "ret to" <+> ppr k) - in if unique_pred k then - tail id (ppcall : prev') Nothing t bs - else - endblock (ppcall) - | Z.Block id' t : bs <- n, id' == k, unique_pred k, - Just (conv, args) <- out, - Just (ress, srt) <- findCopyIn t -> - let call = CmmCall tgt' ress args (CmmSafe srt) CmmMayReturn - tgt' = CmmCallee tgt (cconv_of_conv conv) - delayed = - ptext (sLit "// delayed CopyIn follows previous call") - in tail id (delayed : ppr call : prev') Nothing t bs - | otherwise -> endblock $ with_out out l - findCopyIn (Z.ZTail (CopyIn _ ress srt) _) = Just (ress, srt) - findCopyIn (Z.ZTail _ t) = findCopyIn t - findCopyIn (Z.ZLast _) = Nothing - exit id prev' out n = -- highly irregular (assertion violation?) - let endblock stmt = block' id (stmt : prev') : swallow n in - case out of Nothing -> endblock (text "// ") - Just (conv, args) -> endblock (ppr (CopyOut conv args) $$ - text "// ") - preds = zipPreds g - entry_has_no_pred = case lookupBlockEnv preds (Z.lg_entry g) of - Nothing -> True - Just s -> isEmptyUniqSet s - single_preds = - let add b single = - let id = Z.blockId b - in case lookupBlockEnv preds id of - Nothing -> single - Just s -> if sizeUniqSet s == 1 then - extendBlockSet single id - else single - in Z.fold_blocks add emptyBlockSet g - unique_pred id = elemBlockSet id single_preds - cconv_of_conv (ConventionStandard conv _) = conv - cconv_of_conv (ConventionPrivate {}) = CmmCallConv -- XXX totally bogus - -with_out :: Maybe (Convention, CmmActuals) -> Last -> SDoc -with_out Nothing l = ptext (sLit "??no-arguments??") <+> ppr l -with_out (Just (conv, args)) l = last l - where last (LastCall e k) = - hcat [ptext (sLit "... = foreign "), - doubleQuotes(ppr conv), space, - ppr_target e, parens ( commafy $ map ppr args ), - ptext (sLit " \"safe\""), - case k of Nothing -> ptext (sLit " never returns") - Just _ -> empty, - semi ] - last (LastReturn) = ppr (CmmReturn args) - last (LastJump e) = ppr (CmmJump e args) - last l = ppr (CopyOut conv args) $$ ppr l - ppr_target (CmmLit lit) = ppr lit - ppr_target fn' = parens (ppr fn') - commafy xs = hsep $ punctuate comma xs