module MkGraph
( CmmAGraph
, emptyAGraph, (<*>), catAGraphs, outOfLine
, mkLabel, mkMiddle, mkLast
, withFreshLabel, withUnique, lgraphOfAGraph, labelAGraph
, stackStubExpr
, mkNop, mkAssign, mkStore, mkCall, mkCmmCall, mkSafeCall, mkUnsafeCall, mkFinalCall
, mkJump, mkDirectJump, mkForeignJump, mkJumpGC, mkCbranch, mkSwitch
, mkReturn, mkReturnSimple, mkComment, mkCallEntry
, mkBranch, mkCmmIfThenElse, mkCmmIfThen, mkCmmWhileDo
, copyInOflow, copyInSlot, copyOutOflow, copyOutSlot
)
where
import BlockId
import Cmm
import CmmCallConv (assignArgumentsPos, ParamLocation(..))
import Compiler.Hoopl hiding (Unique, (<*>), mkFirst, mkMiddle, mkLast, mkLabel, mkBranch, Shape(..))
import qualified Compiler.Hoopl as H
import Compiler.Hoopl.GHC (uniqueToLbl)
import FastString
import ForeignCall
import Outputable
import Prelude hiding (succ)
import SMRep (ByteOff)
import StaticFlags
import Unique
import UniqSupply
#include "HsVersions.h"
data CmmGraphOC = Opened (Graph CmmNode O O)
| Closed (Graph CmmNode O C)
type CmmAGraph = UniqSM CmmGraphOC
data Transfer = Call | Jump | Ret deriving Eq
emptyAGraph :: CmmAGraph
(<*>) :: CmmAGraph -> CmmAGraph -> CmmAGraph
catAGraphs :: [CmmAGraph] -> CmmAGraph
mkLabel :: BlockId -> CmmAGraph
mkMiddle :: CmmNode O O -> CmmAGraph
mkLast :: CmmNode O C -> CmmAGraph
withFreshLabel :: String -> (BlockId -> CmmAGraph) -> CmmAGraph
withUnique :: (Unique -> CmmAGraph) -> CmmAGraph
lgraphOfAGraph :: CmmAGraph -> UniqSM CmmGraph
labelAGraph :: BlockId -> CmmAGraph -> UniqSM CmmGraph
mkNop :: CmmAGraph
mkComment :: FastString -> CmmAGraph
mkAssign :: CmmReg -> CmmExpr -> CmmAGraph
mkStore :: CmmExpr -> CmmExpr -> CmmAGraph
mkCall :: CmmExpr -> (Convention, Convention) -> [CmmFormal] -> [CmmActual] ->
UpdFrameOffset -> CmmAGraph
mkCmmCall :: CmmExpr -> [CmmFormal] -> [CmmActual] ->
UpdFrameOffset -> CmmAGraph
mkSafeCall :: ForeignTarget -> [CmmFormal] -> [CmmActual] -> UpdFrameOffset -> Bool -> CmmAGraph
mkUnsafeCall :: ForeignTarget -> [CmmFormal] -> [CmmActual] -> CmmAGraph
mkFinalCall :: CmmExpr -> CCallConv -> [CmmActual] -> UpdFrameOffset -> CmmAGraph
mkJump :: CmmExpr -> [CmmActual] -> UpdFrameOffset -> CmmAGraph
mkDirectJump :: CmmExpr -> [CmmActual] -> UpdFrameOffset -> CmmAGraph
mkJumpGC :: CmmExpr -> [CmmActual] -> UpdFrameOffset -> CmmAGraph
mkForeignJump :: Convention -> CmmExpr -> [CmmActual] -> UpdFrameOffset -> CmmAGraph
mkCbranch :: CmmExpr -> BlockId -> BlockId -> CmmAGraph
mkSwitch :: CmmExpr -> [Maybe BlockId] -> CmmAGraph
mkReturn :: CmmExpr -> [CmmActual] -> UpdFrameOffset -> CmmAGraph
mkReturnSimple :: [CmmActual] -> UpdFrameOffset -> CmmAGraph
mkBranch :: BlockId -> CmmAGraph
mkCmmIfThenElse :: CmmExpr -> CmmAGraph -> CmmAGraph -> CmmAGraph
mkCmmIfThen :: CmmExpr -> CmmAGraph -> CmmAGraph
mkCmmWhileDo :: CmmExpr -> CmmAGraph -> CmmAGraph
outOfLine :: CmmAGraph -> CmmAGraph
emptyAGraph = return $ Opened emptyGraph
ag <*> ah = do g <- ag
h <- ah
return (case (g, h) of
(Opened g, Opened h) -> Opened $ g H.<*> h
(Opened g, Closed h) -> Closed $ g H.<*> h
(Closed g, Opened GNil) -> Closed g
(Closed g, Opened (GUnit e)) -> note_unreachable e $ Closed g
(Closed g, Opened (GMany (JustO e) b x)) -> note_unreachable e $ Opened $ g H.|*><*| GMany NothingO b x
(Closed g, Closed (GMany (JustO e) b x)) -> note_unreachable e $ Closed $ g H.|*><*| GMany NothingO b x
:: CmmGraphOC)
catAGraphs = foldl (<*>) emptyAGraph
outOfLine ag = withFreshLabel "outOfLine" $ \l ->
do g <- ag
return (case g of
Closed (GMany (JustO e) b _) -> note_unreachable e $ Opened $
GMany (JustO $ BLast $ CmmBranch l) b (JustO $ BFirst $ CmmEntry l)
_ -> panic "outOfLine"
:: CmmGraphOC)
note_unreachable :: Block CmmNode O x -> a -> a
note_unreachable block graph =
ASSERT (block_is_empty_or_label)
graph
where block_is_empty_or_label :: Bool
block_is_empty_or_label = case blockToNodeList block of
(NothingC, [], NothingC) -> True
(NothingC, [], JustC (CmmBranch _)) -> True
_ -> False
mkLabel bid = return $ Opened $ H.mkLast (CmmBranch bid) |*><*| H.mkFirst (CmmEntry bid)
mkMiddle middle = return $ Opened $ H.mkMiddle middle
mkLast last = return $ Closed $ H.mkLast last
withUnique f = getUniqueM >>= f
withFreshLabel _name f = getUniqueM >>= f . uniqueToLbl . intToUnique . getKey
lgraphOfAGraph g = do u <- getUniqueM
labelAGraph (mkBlockId u) g
labelAGraph lbl ag = do g <- ag
return $ CmmGraph {g_entry=lbl, g_graph=H.mkFirst (CmmEntry lbl) H.<*> closed g}
where closed :: CmmGraphOC -> Graph CmmNode O C
closed (Closed g) = g
closed (Opened g@(GMany entry body (JustO exit))) =
ASSERT (entryLabel exit `notElem` map entryLabel (postorder_dfs g))
GMany entry body NothingO
closed (Opened _) = panic "labelAGraph"
mkNop = emptyAGraph
mkComment fs = mkMiddle $ CmmComment fs
mkStore l r = mkMiddle $ CmmStore l r
mkAssign l r = if opt_StubDeadValues then assign l r <*> check l else assign l r
where assign l r = mkMiddle (CmmAssign l r)
check (CmmGlobal _) = mkNop
check l@(CmmLocal reg) =
if isGcPtrType ty then
mkCmmIfThen (CmmMachOp (MO_Eq w) [r, stackStubExpr w])
(assign l (CmmLoad (CmmLit (CmmInt 0 w)) ty))
else mkNop
where ty = localRegType reg
w = typeWidth ty
r = CmmReg l
mkCbranch pred ifso ifnot = mkLast (CmmCondBranch pred ifso ifnot)
mkSwitch e tbl = mkLast $ CmmSwitch e tbl
mkSafeCall t fs as upd i = withFreshLabel "safe call" $ body
where
body k =
( mkStore (CmmStackSlot (CallArea (Young k)) (widthInBytes wordWidth))
(CmmLit (CmmBlock k))
<*> mkLast (CmmForeignCall {tgt=t, res=fs, args=as, succ=k, updfr=upd, intrbl=i})
<*> mkLabel k)
mkUnsafeCall t fs as = mkMiddle $ CmmUnsafeForeignCall t fs as
mkBranch bid = mkLast (CmmBranch bid)
mkCmmIfThenElse e tbranch fbranch =
withFreshLabel "end of if" $ \endif ->
withFreshLabel "start of then" $ \tid ->
withFreshLabel "start of else" $ \fid ->
mkCbranch e tid fid <*>
mkLabel tid <*> tbranch <*> mkBranch endif <*>
mkLabel fid <*> fbranch <*> mkLabel endif
mkCmmIfThen e tbranch
= withFreshLabel "end of if" $ \endif ->
withFreshLabel "start of then" $ \tid ->
mkCbranch e tid endif <*>
mkLabel tid <*> tbranch <*> mkLabel endif
mkCmmWhileDo e body =
withFreshLabel "loop test" $ \test ->
withFreshLabel "loop head" $ \head ->
withFreshLabel "end while" $ \endwhile ->
mkBranch test <*> mkLabel head <*> body
<*> mkLabel test <*> mkCbranch e head endwhile
<*> mkLabel endwhile
stackStubExpr :: Width -> CmmExpr
stackStubExpr w = CmmLit (CmmInt 0 w)
copyInOflow :: Convention -> Area -> [CmmFormal] -> (Int, CmmAGraph)
copyInSlot :: Convention -> [CmmFormal] -> [CmmNode O O]
copyOutSlot :: Convention -> [LocalReg] -> [CmmNode O O]
copyInOflow conv area formals = (offset, catAGraphs $ map mkMiddle nodes)
where (offset, nodes) = copyIn oneCopyOflowI conv area formals
copyInSlot c f = snd $ copyIn oneCopySlotI c (panic "no area for copying to slots") f
type SlotCopier = Area -> (LocalReg, ByteOff) -> (ByteOff, [CmmNode O O]) ->
(ByteOff, [CmmNode O O])
type CopyIn = SlotCopier -> Convention -> Area -> [CmmFormal] -> (ByteOff, [CmmNode O O])
copyIn :: CopyIn
copyIn oflow conv area formals =
foldr ci (init_offset, []) args'
where ci (reg, RegisterParam r) (n, ms) =
(n, CmmAssign (CmmLocal reg) (CmmReg $ CmmGlobal r) : ms)
ci (r, StackParam off) (n, ms) = oflow area (r, off) (n, ms)
init_offset = widthInBytes wordWidth
args = assignArgumentsPos conv localRegType formals
args' = foldl adjust [] args
where adjust rst (v, StackParam off) = (v, StackParam (off + init_offset)) : rst
adjust rst x@(_, RegisterParam _) = x : rst
oneCopyOflowI, oneCopySlotI :: SlotCopier
oneCopyOflowI area (reg, off) (n, ms) =
(max n off, CmmAssign (CmmLocal reg) (CmmLoad (CmmStackSlot area off) ty) : ms)
where ty = localRegType reg
oneCopySlotI _ (reg, _) (n, ms) =
(n, CmmAssign (CmmLocal reg) (CmmLoad (CmmStackSlot (RegSlot reg) w) ty) : ms)
where ty = localRegType reg
w = widthInBytes (typeWidth ty)
copyOutOflow :: Convention -> Transfer -> Area -> [CmmActual] -> UpdFrameOffset ->
(Int, CmmAGraph)
copyOutOflow conv transfer area@(CallArea a) actuals updfr_off
= foldr co (init_offset, emptyAGraph) args'
where
co (v, RegisterParam r) (n, ms) = (n, mkAssign (CmmGlobal r) v <*> ms)
co (v, StackParam off) (n, ms) = (max n off, mkStore (CmmStackSlot area off) v <*> ms)
(setRA, init_offset) =
case a of Young id -> id `seq`
if transfer == Call then
([(CmmLit (CmmBlock id), StackParam init_offset)],
widthInBytes wordWidth)
else ([], 0)
Old -> ([], updfr_off)
args :: [(CmmExpr, ParamLocation)]
args = assignArgumentsPos conv cmmExprType actuals
args' = foldl adjust setRA args
where adjust rst (v, StackParam off) = (v, StackParam (off + init_offset)) : rst
adjust rst x@(_, RegisterParam _) = x : rst
copyOutOflow _ _ (RegSlot _) _ _ = panic "cannot copy arguments into a register slot"
copyOutSlot conv actuals = foldr co [] args
where co (v, RegisterParam r) ms = CmmAssign (CmmGlobal r) (toExp v) : ms
co (v, StackParam off) ms = CmmStore (CmmStackSlot (RegSlot v) off) (toExp v) : ms
toExp r = CmmReg (CmmLocal r)
args = assignArgumentsPos conv localRegType actuals
mkCallEntry :: Convention -> [CmmFormal] -> (Int, CmmAGraph)
mkCallEntry conv formals = copyInOflow conv (CallArea Old) formals
lastWithArgs :: Transfer -> Area -> Convention -> [CmmActual] -> UpdFrameOffset ->
(ByteOff -> CmmAGraph) -> CmmAGraph
lastWithArgs transfer area conv actuals updfr_off last =
let (outArgs, copies) = copyOutOflow conv transfer area actuals updfr_off in
copies <*> last outArgs
old :: Area
old = CallArea Old
toCall :: CmmExpr -> Maybe BlockId -> UpdFrameOffset -> ByteOff -> ByteOff -> CmmAGraph
toCall e cont updfr_off res_space arg_space =
mkLast $ CmmCall e cont arg_space res_space updfr_off
mkJump e actuals updfr_off =
lastWithArgs Jump old NativeNodeCall actuals updfr_off $ toCall e Nothing updfr_off 0
mkDirectJump e actuals updfr_off =
lastWithArgs Jump old NativeDirectCall actuals updfr_off $ toCall e Nothing updfr_off 0
mkJumpGC e actuals updfr_off =
lastWithArgs Jump old GC actuals updfr_off $ toCall e Nothing updfr_off 0
mkForeignJump conv e actuals updfr_off =
lastWithArgs Jump old conv actuals updfr_off $ toCall e Nothing updfr_off 0
mkReturn e actuals updfr_off =
lastWithArgs Ret old NativeReturn actuals updfr_off $ toCall e Nothing updfr_off 0
mkReturnSimple actuals updfr_off =
lastWithArgs Ret old NativeReturn actuals updfr_off $ toCall e Nothing updfr_off 0
where e = CmmLoad (CmmStackSlot (CallArea Old) updfr_off) gcWord
mkFinalCall f _ actuals updfr_off =
lastWithArgs Call old NativeDirectCall actuals updfr_off $ toCall f Nothing updfr_off 0
mkCmmCall f results actuals = mkCall f (NativeDirectCall, NativeReturn) results actuals
mkCall f (callConv, retConv) results actuals updfr_off =
withFreshLabel "call successor" $ \k ->
let area = CallArea $ Young k
(off, copyin) = copyInOflow retConv area results
copyout = lastWithArgs Call area callConv actuals updfr_off
(toCall f (Just k) updfr_off off)
in (copyout <*> mkLabel k <*> copyin)