module CmmProcPoint
( ProcPointSet, Status(..)
, callProcPoints, minimalProcPointSet
, addProcPointProtocols, splitAtProcPoints, procPointAnalysis
)
where
import Prelude hiding (last, unzip, succ, zip)
import BlockId
import CLabel
import Cmm
import CmmDecl
import CmmExpr
import CmmContFlowOpt
import CmmInfo
import CmmLive
import Constants
import Data.List (sortBy)
import Maybes
import MkGraph
import Control.Monad
import OptimizationFuel
import Outputable
import Platform
import UniqSet
import UniqSupply
import Compiler.Hoopl
import qualified Data.Map as Map
type ProcPointSet = BlockSet
data Status
= ReachedBy ProcPointSet
| ProcPoint
instance Outputable Status where
ppr (ReachedBy ps)
| setNull ps = text "<not-reached>"
| otherwise = text "reached by" <+>
(hsep $ punctuate comma $ map ppr $ setElems ps)
ppr ProcPoint = text "<procpt>"
lattice :: DataflowLattice Status
lattice = DataflowLattice "direct proc-point reachability" unreached add_to
where unreached = ReachedBy setEmpty
add_to _ (OldFact ProcPoint) _ = (NoChange, ProcPoint)
add_to _ _ (NewFact ProcPoint) = (SomeChange, ProcPoint)
add_to _ (OldFact (ReachedBy p)) (NewFact (ReachedBy p')) =
let union = setUnion p' p
in if setSize union > setSize p then (SomeChange, ReachedBy union)
else (NoChange, ReachedBy p)
forward :: FwdTransfer CmmNode Status
forward = mkFTransfer3 first middle ((mkFactBase lattice . ) . last)
where first :: CmmNode C O -> Status -> Status
first (CmmEntry id) ProcPoint = ReachedBy $ setSingleton id
first _ x = x
middle _ x = x
last :: CmmNode O C -> Status -> [(Label, Status)]
last (CmmCall {cml_cont = Just k}) _ = [(k, ProcPoint)]
last (CmmForeignCall {succ = k}) _ = [(k, ProcPoint)]
last l x = map (\id -> (id, x)) (successors l)
callProcPoints :: CmmGraph -> ProcPointSet
callProcPoints g = foldGraphBlocks add (setSingleton (g_entry g)) g
where add :: CmmBlock -> BlockSet -> BlockSet
add b set = case lastNode b of
CmmCall {cml_cont = Just k} -> setInsert k set
CmmForeignCall {succ=k} -> setInsert k set
_ -> set
minimalProcPointSet :: Platform -> ProcPointSet -> CmmGraph -> FuelUniqSM ProcPointSet
minimalProcPointSet platform callProcPoints g = extendPPSet platform g (postorderDfs g) callProcPoints
procPointAnalysis :: ProcPointSet -> CmmGraph -> FuelUniqSM (BlockEnv Status)
procPointAnalysis procPoints g =
liftM snd $ dataflowPassFwd g initProcPoints $ analFwd lattice forward
where initProcPoints = [(id, ProcPoint) | id <- setElems procPoints]
extendPPSet :: Platform -> CmmGraph -> [CmmBlock] -> ProcPointSet -> FuelUniqSM ProcPointSet
extendPPSet platform g blocks procPoints =
do env <- procPointAnalysis procPoints g
let add block pps = let id = entryLabel block
in case mapLookup id env of
Just ProcPoint -> setInsert id pps
_ -> pps
procPoints' = foldGraphBlocks add setEmpty g
newPoints = mapMaybe ppSuccessor blocks
newPoint = listToMaybe newPoints
ppSuccessor b =
let nreached id = case mapLookup id env `orElse`
pprPanic "no ppt" (ppr id <+> pprPlatform platform b) of
ProcPoint -> 1
ReachedBy ps -> setSize ps
block_procpoints = nreached (entryLabel b)
newId succ_id = not (setMember succ_id procPoints') &&
nreached succ_id > block_procpoints
in listToMaybe $ filter newId $ successors b
case newPoint of Just id ->
if setMember id procPoints' then panic "added old proc pt"
else extendPPSet platform g blocks (setInsert id procPoints')
Nothing -> return procPoints'
data Protocol = Protocol Convention [CmmFormal] Area
deriving Eq
instance Outputable Protocol where
ppr (Protocol c fs a) = text "Protocol" <+> ppr c <+> ppr fs <+> ppr a
addProcPointProtocols :: ProcPointSet -> ProcPointSet -> CmmGraph -> FuelUniqSM CmmGraph
addProcPointProtocols callPPs procPoints g =
do liveness <- cmmLiveness g
(protos, g') <- optimize_calls liveness g
blocks'' <- add_CopyOuts protos procPoints g'
return $ ofBlockMap (g_entry g) blocks''
where optimize_calls liveness g =
do let (protos, blocks') =
foldGraphBlocks maybe_add_call (mapEmpty, mapEmpty) g
protos' = add_unassigned liveness procPoints protos
let g' = ofBlockMap (g_entry g) (add_CopyIns callPPs protos' blocks')
return (protos', removeUnreachableBlocks g')
maybe_add_call :: CmmBlock -> (BlockEnv Protocol, BlockEnv CmmBlock)
-> (BlockEnv Protocol, BlockEnv CmmBlock)
maybe_add_call block (protos, blocks) =
case lastNode block of
CmmCall tgt (Just k) args res s
| Just proto <- mapLookup k protos,
Just pee <- branchesToProcPoint k
-> let newblock = replaceLastNode block (CmmCall tgt (Just pee)
args res s)
changed_blocks = insertBlock newblock blocks
unchanged_blocks = insertBlock block blocks
in case mapLookup pee protos of
Nothing -> (mapInsert pee proto protos, changed_blocks)
Just proto' ->
if proto == proto' then (protos, changed_blocks)
else (protos, unchanged_blocks)
_ -> (protos, insertBlock block blocks)
branchesToProcPoint :: BlockId -> Maybe BlockId
branchesToProcPoint id =
let block = mapLookup id (toBlockMap g) `orElse`
panic "branch out of graph"
in case blockToNodeList block of
#if __GLASGOW_HASKELL__ >= 612
(_, [], JustC (CmmBranch pee)) | setMember pee procPoints -> Just pee
_ -> Nothing
#else
(_, [], exit) | CmmBranch pee <- getItOut exit
, setMember pee procPoints -> Just pee
_ -> Nothing
where
getItOut :: MaybeC C a -> a
getItOut (JustC a) = a
#endif
add_unassigned :: BlockEnv CmmLive -> ProcPointSet -> BlockEnv Protocol ->
BlockEnv Protocol
add_unassigned = pass_live_vars_as_args
pass_live_vars_as_args :: BlockEnv CmmLive -> ProcPointSet ->
BlockEnv Protocol -> BlockEnv Protocol
pass_live_vars_as_args _liveness procPoints protos = protos'
where protos' = setFold addLiveVars protos procPoints
addLiveVars :: BlockId -> BlockEnv Protocol -> BlockEnv Protocol
addLiveVars id protos =
case mapLookup id protos of
Just _ -> protos
Nothing -> let live = emptyRegSet
formals = uniqSetToList live
prot = Protocol Private formals $ CallArea $ Young id
in mapInsert id prot protos
add_CopyIns :: ProcPointSet -> BlockEnv Protocol -> BlockEnv CmmBlock -> BlockEnv CmmBlock
add_CopyIns callPPs protos blocks = mapFold maybe_insert_CopyIns mapEmpty blocks
where maybe_insert_CopyIns block blocks
| not $ setMember bid callPPs
, Just (Protocol c fs _area) <- mapLookup bid protos
= let nodes = copyInSlot c fs
(h, m, l) = blockToNodeList block
in insertBlock (blockOfNodeList (h, nodes ++ m, l)) blocks
| otherwise = insertBlock block blocks
where bid = entryLabel block
add_CopyOuts :: BlockEnv Protocol -> ProcPointSet -> CmmGraph ->
FuelUniqSM (BlockEnv CmmBlock)
add_CopyOuts protos procPoints g = foldGraphBlocks mb_copy_out (return mapEmpty) g
where mb_copy_out :: CmmBlock -> FuelUniqSM (BlockEnv CmmBlock) ->
FuelUniqSM (BlockEnv CmmBlock)
mb_copy_out b z | entryLabel b == g_entry g = skip b z
mb_copy_out b z =
case lastNode b of
CmmCall {} -> skip b z
CmmForeignCall {} -> skip b z
_ -> copy_out b z
copy_out b z = foldr trySucc init (successors b) >>= finish
where init = (\bmap -> (b, bmap)) `liftM` z
trySucc succId z =
if setMember succId procPoints then
case mapLookup succId protos of
Nothing -> z
Just (Protocol c fs _area) -> insert z succId $ copyOutSlot c fs
else z
insert z succId m =
do (b, bmap) <- z
(b, bs) <- insertBetween b m succId
return $ (b, foldl (flip insertBlock) bmap bs)
finish (b, bmap) = return $ insertBlock b bmap
skip b bs = insertBlock b `liftM` bs
splitAtProcPoints :: CLabel -> ProcPointSet-> ProcPointSet -> BlockEnv Status ->
CmmTop -> FuelUniqSM [CmmTop]
splitAtProcPoints entry_label callPPs procPoints procMap
(CmmProc (TopInfo {info_tbl=info_tbl,
stack_info=stack_info})
top_l g@(CmmGraph {g_entry=entry})) =
do
let addBlock b graphEnv =
case mapLookup bid procMap of
Just ProcPoint -> add graphEnv bid bid b
Just (ReachedBy set) ->
case setElems set of
[] -> graphEnv
[id] -> add graphEnv id bid b
_ -> panic "Each block should be reachable from only one ProcPoint"
Nothing -> pprPanic "block not reached by a proc point?" (ppr bid)
where bid = entryLabel b
add graphEnv procId bid b = mapInsert procId graph' graphEnv
where graph = mapLookup procId graphEnv `orElse` mapEmpty
graph' = mapInsert bid b graph
graphEnv <- return $ foldGraphBlocks addBlock emptyBlockMap g
let add_label map pp = Map.insert pp lbls map
where lbls | pp == entry = (entry_label, Just entry_info_lbl)
| otherwise = (blockLbl pp, guard (setMember pp callPPs) >> Just (infoTblLbl pp))
entry_info_lbl = case info_tbl of
CmmInfoTable entry_info_label _ _ _ _ -> entry_info_label
CmmNonInfoTable -> pprPanic "splitAtProcPoints: looked at info label for entry without info table" (ppr pp)
procLabels = foldl add_label Map.empty
(filter (flip mapMember (toBlockMap g)) (setElems procPoints))
let add_sp_off :: CmmBlock -> BlockEnv CmmStackInfo -> BlockEnv CmmStackInfo
add_sp_off b env =
case lastNode b of
CmmCall {cml_cont = Just succ, cml_ret_args = off, cml_ret_off = updfr_off} ->
mapInsert succ (StackInfo { arg_space = off, updfr_space = Just updfr_off}) env
CmmForeignCall {succ = succ, updfr = updfr_off} ->
mapInsert succ (StackInfo { arg_space = wORD_SIZE, updfr_space = Just updfr_off}) env
_ -> env
spEntryMap = foldGraphBlocks add_sp_off (mapInsert entry stack_info emptyBlockMap) g
getStackInfo id = mapLookup id spEntryMap `orElse` StackInfo {arg_space = 0, updfr_space = Nothing}
let add_jump_block (env, bs) (pp, l) =
do bid <- liftM mkBlockId getUniqueM
let b = blockOfNodeList (JustC (CmmEntry bid), [], JustC jump)
StackInfo {arg_space = argSpace, updfr_space = off} = getStackInfo pp
jump = CmmCall (CmmLit (CmmLabel l)) Nothing argSpace 0
(off `orElse` 0)
return (mapInsert pp bid env, b : bs)
add_jumps (newGraphEnv) (ppId, blockEnv) =
do let needed_jumps =
mapFold add_if_branch_to_pp [] blockEnv
add_if_branch_to_pp :: CmmBlock -> [(BlockId, CLabel)] -> [(BlockId, CLabel)]
add_if_branch_to_pp block rst =
case lastNode block of
CmmBranch id -> add_if_pp id rst
CmmCondBranch _ ti fi -> add_if_pp ti (add_if_pp fi rst)
CmmSwitch _ tbl -> foldr add_if_pp rst (catMaybes tbl)
_ -> rst
add_if_pp id rst = case Map.lookup id procLabels of
Just (lbl, mb_info_lbl) -> (id, mb_info_lbl `orElse` lbl) : rst
Nothing -> rst
(jumpEnv, jumpBlocks) <-
foldM add_jump_block (mapEmpty, []) needed_jumps
let b = expectJust "block in env" $ mapLookup ppId blockEnv
off = getStackInfo ppId
blockEnv' = mapInsert ppId b blockEnv
blockEnv'' = toBlockMap $ replaceBranches jumpEnv $ ofBlockMap ppId blockEnv'
blockEnv''' = foldl (flip insertBlock) blockEnv'' jumpBlocks
let g' = (off, ofBlockMap ppId blockEnv''')
return (mapInsert ppId g' newGraphEnv)
graphEnv <- foldM add_jumps emptyBlockMap $ mapToList graphEnv
let to_proc (bid, (stack_info, g)) = case expectJust "pp label" $ Map.lookup bid procLabels of
(lbl, Just info_lbl)
| bid == entry
-> CmmProc (TopInfo {info_tbl=info_tbl, stack_info=stack_info})
top_l (replacePPIds g)
| otherwise
-> CmmProc (TopInfo {info_tbl=mkEmptyContInfoTable info_lbl, stack_info=stack_info})
lbl (replacePPIds g)
(lbl, Nothing)
-> CmmProc (TopInfo {info_tbl=CmmNonInfoTable, stack_info=stack_info})
lbl (replacePPIds g)
replacePPIds g = mapGraphNodes (id, mapExp repl, mapExp repl) g
where repl e@(CmmLit (CmmBlock bid)) =
case Map.lookup bid procLabels of
Just (_, Just info_lbl) -> CmmLit (CmmLabel info_lbl)
_ -> e
repl e = e
let (_, block_order) = foldl add_block_num (0::Int, emptyBlockMap) (postorderDfs g)
add_block_num (i, map) block = (i+1, mapInsert (entryLabel block) i map)
sort_fn (bid, _) (bid', _) =
compare (expectJust "block_order" $ mapLookup bid block_order)
(expectJust "block_order" $ mapLookup bid' block_order)
procs <- return $ map to_proc $ sortBy sort_fn $ mapToList graphEnv
return
procs
splitAtProcPoints _ _ _ _ t@(CmmData _ _) = return [t]