mapBlockTop, mapBlockTopM,
mapGenBlockTop, mapGenBlockTopM,
stripLive,
- spillNatBlock,
+ stripLiveBlock,
slurpConflicts,
slurpReloadCoalesce,
eraseDeltasLive,
) where
+
+import Reg
+import Instruction
+
import BlockId
-import Regs
-import Instrs
-import PprMach
-import RegAllocInfo
import Cmm hiding (RegSet)
+import PprCmm()
import Digraph
import Outputable
-- | A top level thing which carries liveness information.
-type LiveCmmTop
+type LiveCmmTop instr
= GenCmmTop
CmmStatic
LiveInfo
- (ListGraph (GenBasicBlock LiveInstr))
- -- the "instructions" here are actually more blocks,
- -- single blocks are acyclic
- -- multiple blocks are taken to be cyclic.
+ [SCC (LiveBasicBlock instr)]
+
-- | An instruction with liveness information.
-data LiveInstr
- = Instr Instr (Maybe Liveness)
+data LiveInstr instr
+ = Instr instr (Maybe Liveness)
+
+ -- | spill this reg to a stack slot
+ | SPILL Reg Int
+
+ -- | reload this reg from a stack slot
+ | RELOAD Int Reg
+
-- | Liveness information.
-- The regs which die are ones which are no longer live in the *next* instruction
-- | Stash regs live on entry to each basic block in the info part of the cmm code.
data LiveInfo
= LiveInfo
- [CmmStatic] -- cmm static stuff
- (Maybe BlockId) -- id of the first block
- (BlockMap RegSet) -- argument locals live on entry to this block
+ [CmmStatic] -- cmm static stuff
+ (Maybe BlockId) -- id of the first block
+ (Maybe (BlockMap RegSet)) -- argument locals live on entry to this block
-- | A basic block with liveness information.
-type LiveBasicBlock
- = GenBasicBlock LiveInstr
-
+type LiveBasicBlock instr
+ = GenBasicBlock (LiveInstr instr)
+
+
+instance Outputable instr
+ => Outputable (LiveInstr instr) where
+ ppr (SPILL reg slot)
+ = hcat [
+ ptext (sLit "\tSPILL"),
+ char ' ',
+ ppr reg,
+ comma,
+ ptext (sLit "SLOT") <> parens (int slot)]
+
+ ppr (RELOAD slot reg)
+ = hcat [
+ ptext (sLit "\tRELOAD"),
+ char ' ',
+ ptext (sLit "SLOT") <> parens (int slot),
+ comma,
+ ppr reg]
-instance Outputable LiveInstr where
ppr (Instr instr Nothing)
= ppr instr
where pprRegs :: SDoc -> RegSet -> SDoc
pprRegs name regs
| isEmptyUniqSet regs = empty
- | otherwise = name <> (hcat $ punctuate space $ map (docToSDoc . pprUserReg) $ uniqSetToList regs)
-
+ | otherwise = name <> (hcat $ punctuate space $ map ppr $ uniqSetToList regs)
instance Outputable LiveInfo where
ppr (LiveInfo static firstId liveOnEntry)
$$ text "# liveOnEntry = " <> ppr liveOnEntry
+
-- | map a function across all the basic blocks in this code
--
mapBlockTop
- :: (LiveBasicBlock -> LiveBasicBlock)
- -> LiveCmmTop -> LiveCmmTop
+ :: (LiveBasicBlock instr -> LiveBasicBlock instr)
+ -> LiveCmmTop instr -> LiveCmmTop instr
mapBlockTop f cmm
= evalState (mapBlockTopM (\x -> return $ f x) cmm) ()
--
mapBlockTopM
:: Monad m
- => (LiveBasicBlock -> m LiveBasicBlock)
- -> LiveCmmTop -> m LiveCmmTop
+ => (LiveBasicBlock instr -> m (LiveBasicBlock instr))
+ -> LiveCmmTop instr -> m (LiveCmmTop instr)
mapBlockTopM _ cmm@(CmmData{})
= return cmm
-mapBlockTopM f (CmmProc header label params (ListGraph comps))
- = do comps' <- mapM (mapBlockCompM f) comps
- return $ CmmProc header label params (ListGraph comps')
+mapBlockTopM f (CmmProc header label params sccs)
+ = do sccs' <- mapM (mapSCCM f) sccs
+ return $ CmmProc header label params sccs'
+
+mapSCCM :: Monad m => (a -> m b) -> SCC a -> m (SCC b)
+mapSCCM f (AcyclicSCC x)
+ = do x' <- f x
+ return $ AcyclicSCC x'
+mapSCCM f (CyclicSCC xs)
+ = do xs' <- mapM f xs
+ return $ CyclicSCC xs'
+
+{-
mapBlockCompM :: Monad m => (a -> m a') -> (GenBasicBlock a) -> m (GenBasicBlock a')
mapBlockCompM f (BasicBlock i blocks)
= do blocks' <- mapM f blocks
return $ BasicBlock i blocks'
-
+-}
-- map a function across all the basic blocks in this code
mapGenBlockTop
-- Slurping of conflicts and moves is wrapped up together so we don't have
-- to make two passes over the same code when we want to build the graph.
--
-slurpConflicts :: LiveCmmTop -> (Bag (UniqSet Reg), Bag (Reg, Reg))
+slurpConflicts
+ :: Instruction instr
+ => LiveCmmTop instr
+ -> (Bag (UniqSet Reg), Bag (Reg, Reg))
+
slurpConflicts live
= slurpCmm (emptyBag, emptyBag) live
where slurpCmm rs CmmData{} = rs
- slurpCmm rs (CmmProc info _ _ (ListGraph blocks))
- = foldl' (slurpComp info) rs blocks
+ slurpCmm rs (CmmProc info _ _ sccs)
+ = foldl' (slurpSCC info) rs sccs
- slurpComp info rs (BasicBlock _ blocks)
- = foldl' (slurpBlock info) rs blocks
+ slurpSCC info rs (AcyclicSCC b)
+ = slurpBlock info rs b
+
+ slurpSCC info rs (CyclicSCC bs)
+ = foldl' (slurpBlock info) rs bs
slurpBlock info rs (BasicBlock blockId instrs)
- | LiveInfo _ _ blockLive <- info
+ | LiveInfo _ _ (Just blockLive) <- info
, Just rsLiveEntry <- lookupBlockEnv blockLive blockId
, (conflicts, moves) <- slurpLIs rsLiveEntry rs instrs
= (consBag rsLiveEntry conflicts, moves)
| otherwise
- = panic "RegLiveness.slurpBlock: bad block"
+ = panic "Liveness.slurpConflicts: bad block"
slurpLIs rsLive (conflicts, moves) []
= (consBag rsLive conflicts, moves)
- slurpLIs rsLive rs (Instr _ Nothing : lis) = slurpLIs rsLive rs lis
+ slurpLIs rsLive rs (Instr _ Nothing : lis)
+ = slurpLIs rsLive rs lis
+
+ -- we're not expecting to be slurping conflicts from spilled code
+ slurpLIs _ _ (SPILL _ _ : _)
+ = panic "Liveness.slurpConflicts: unexpected SPILL"
+
+ slurpLIs _ _ (RELOAD _ _ : _)
+ = panic "Liveness.slurpConflicts: unexpected RELOAD"
slurpLIs rsLiveEntry (conflicts, moves) (Instr instr (Just live) : lis)
= let
--
rsConflicts = unionUniqSets rsLiveNext rsOrphans
- in case isRegRegMove instr of
+ in case takeRegRegMoveInstr instr of
Just rr -> slurpLIs rsLiveNext
( consBag rsConflicts conflicts
, consBag rr moves) lis
-- the spill\/reload instrs can be cleaned and replaced by a nop reg-reg move.
--
--
-slurpReloadCoalesce :: LiveCmmTop -> Bag (Reg, Reg)
+slurpReloadCoalesce
+ :: Instruction instr
+ => LiveCmmTop instr
+ -> Bag (Reg, Reg)
+
slurpReloadCoalesce live
= slurpCmm emptyBag live
where slurpCmm cs CmmData{} = cs
- slurpCmm cs (CmmProc _ _ _ (ListGraph blocks))
- = foldl' slurpComp cs blocks
+ slurpCmm cs (CmmProc _ _ _ sccs)
+ = slurpComp cs (flattenSCCs sccs)
- slurpComp cs comp
- = let (moveBags, _) = runState (slurpCompM comp) emptyUFM
+ slurpComp cs blocks
+ = let (moveBags, _) = runState (slurpCompM blocks) emptyUFM
in unionManyBags (cs : moveBags)
- slurpCompM (BasicBlock _ blocks)
+ slurpCompM blocks
= do -- run the analysis once to record the mapping across jumps.
mapM_ (slurpBlock False) blocks
(_, mMoves) <- mapAccumLM slurpLI slotMap instrs
return $ listToBag $ catMaybes mMoves
- slurpLI :: UniqFM Reg -- current slotMap
- -> LiveInstr
+ slurpLI :: Instruction instr
+ => UniqFM Reg -- current slotMap
+ -> LiveInstr instr
-> State (UniqFM [UniqFM Reg]) -- blockId -> [slot -> reg]
-- for tracking slotMaps across jumps
( UniqFM Reg -- new slotMap
, Maybe (Reg, Reg)) -- maybe a new coalesce edge
- slurpLI slotMap (Instr instr _)
+ slurpLI slotMap li
-- remember what reg was stored into the slot
- | SPILL reg slot <- instr
+ | SPILL reg slot <- li
, slotMap' <- addToUFM slotMap slot reg
= return (slotMap', Nothing)
-- add an edge betwen the this reg and the last one stored into the slot
- | RELOAD slot reg <- instr
+ | RELOAD slot reg <- li
= case lookupUFM slotMap slot of
Just reg2
| reg /= reg2 -> return (slotMap, Just (reg, reg2))
Nothing -> return (slotMap, Nothing)
-- if we hit a jump, remember the current slotMap
- | targets <- jumpDests instr []
+ | Instr instr _ <- li
+ , targets <- jumpDestsOfInstr instr
, not $ null targets
= do mapM_ (accSlotMap slotMap) targets
return (slotMap, Nothing)
-- | Strip away liveness information, yielding NatCmmTop
-stripLive :: LiveCmmTop -> NatCmmTop
+stripLive
+ :: Instruction instr
+ => LiveCmmTop instr
+ -> NatCmmTop instr
+
stripLive live
= stripCmm live
where stripCmm (CmmData sec ds) = CmmData sec ds
- stripCmm (CmmProc (LiveInfo info _ _) label params (ListGraph comps))
+ stripCmm (CmmProc (LiveInfo info _ _) label params sccs)
= CmmProc info label params
- (ListGraph $ concatMap stripComp comps)
-
- stripComp (BasicBlock _ blocks) = map stripBlock blocks
- stripBlock (BasicBlock i instrs) = BasicBlock i (map stripLI instrs)
- stripLI (Instr instr _) = instr
+ (ListGraph $ map stripLiveBlock $ flattenSCCs sccs)
+
+-- | Strip away liveness information from a basic block,
+-- and make real spill instructions out of SPILL, RELOAD pseudos along the way.
--- | Make real spill instructions out of SPILL, RELOAD pseudos
+stripLiveBlock
+ :: Instruction instr
+ => LiveBasicBlock instr
+ -> NatBasicBlock instr
-spillNatBlock :: NatBasicBlock -> NatBasicBlock
-spillNatBlock (BasicBlock i is)
+stripLiveBlock (BasicBlock i lis)
= BasicBlock i instrs'
+
where (instrs', _)
- = runState (spillNat [] is) 0
+ = runState (spillNat [] lis) 0
spillNat acc []
= return (reverse acc)
- spillNat acc (DELTA i : instrs)
- = do put i
- spillNat acc instrs
-
spillNat acc (SPILL reg slot : instrs)
= do delta <- get
spillNat (mkSpillInstr reg delta slot : acc) instrs
= do delta <- get
spillNat (mkLoadInstr reg delta slot : acc) instrs
- spillNat acc (instr : instrs)
+ spillNat acc (Instr instr _ : instrs)
+ | Just i <- takeDeltaInstr instr
+ = do put i
+ spillNat acc instrs
+
+ spillNat acc (Instr instr _ : instrs)
= spillNat (instr : acc) instrs
-- | Erase Delta instructions.
-eraseDeltasLive :: LiveCmmTop -> LiveCmmTop
+eraseDeltasLive
+ :: Instruction instr
+ => LiveCmmTop instr
+ -> LiveCmmTop instr
+
eraseDeltasLive cmm
= mapBlockTop eraseBlock cmm
where
- isDelta (DELTA _) = True
- isDelta _ = False
-
eraseBlock (BasicBlock id lis)
= BasicBlock id
- $ filter (\(Instr i _) -> not $ isDelta i)
+ $ filter (\(Instr i _) -> not $ isJust $ takeDeltaInstr i)
$ lis
-- also erase reg -> reg moves when the destination dies in this instr.
patchEraseLive
- :: (Reg -> Reg)
- -> LiveCmmTop -> LiveCmmTop
+ :: Instruction instr
+ => (Reg -> Reg)
+ -> LiveCmmTop instr -> LiveCmmTop instr
patchEraseLive patchF cmm
= patchCmm cmm
where
patchCmm cmm@CmmData{} = cmm
- patchCmm (CmmProc info label params (ListGraph comps))
- | LiveInfo static id blockMap <- info
- = let patchRegSet set = mkUniqSet $ map patchF $ uniqSetToList set
+ patchCmm (CmmProc info label params sccs)
+ | LiveInfo static id (Just blockMap) <- info
+ = let
+ patchRegSet set = mkUniqSet $ map patchF $ uniqSetToList set
blockMap' = mapBlockEnv patchRegSet blockMap
- info' = LiveInfo static id blockMap'
- in CmmProc info' label params $ ListGraph $ map patchComp comps
+ info' = LiveInfo static id (Just blockMap')
+ in CmmProc info' label params $ map patchSCC sccs
- patchComp (BasicBlock id blocks)
- = BasicBlock id $ map patchBlock blocks
+ | otherwise
+ = panic "RegAlloc.Liveness.patchEraseLive: no blockMap"
+
+ patchSCC (AcyclicSCC b) = AcyclicSCC (patchBlock b)
+ patchSCC (CyclicSCC bs) = CyclicSCC (map patchBlock bs)
patchBlock (BasicBlock id lis)
= BasicBlock id $ patchInstrs lis
patchInstrs (li : lis)
| Instr i (Just live) <- li'
- , Just (r1, r2) <- isRegRegMove i
+ , Just (r1, r2) <- takeRegRegMoveInstr i
, eatMe r1 r2 live
= patchInstrs lis
-- | Patch registers in this LiveInstr, including the liveness information.
--
patchRegsLiveInstr
- :: (Reg -> Reg)
- -> LiveInstr -> LiveInstr
+ :: Instruction instr
+ => (Reg -> Reg)
+ -> LiveInstr instr -> LiveInstr instr
patchRegsLiveInstr patchF li
= case li of
Instr instr Nothing
- -> Instr (patchRegs instr patchF) Nothing
+ -> Instr (patchRegsOfInstr instr patchF) Nothing
Instr instr (Just live)
-> Instr
- (patchRegs instr patchF)
+ (patchRegsOfInstr instr patchF)
(Just live
{ -- WARNING: have to go via lists here because patchF changes the uniq in the Reg
liveBorn = mkUniqSet $ map patchF $ uniqSetToList $ liveBorn live
, liveDieRead = mkUniqSet $ map patchF $ uniqSetToList $ liveDieRead live
, liveDieWrite = mkUniqSet $ map patchF $ uniqSetToList $ liveDieWrite live })
+ SPILL reg slot
+ -> SPILL (patchF reg) slot
+
+ RELOAD slot reg
+ -> RELOAD slot (patchF reg)
+
+
+--------------------------------------------------------------------------------
+-- | Convert a NatCmmTop to a LiveCmmTop, with empty liveness information
+{-
+natCmmTopToLive
+ :: NatCmmTop instr
+ -> LiveCmmTop instr
+
+natCmmTopToLive cmm@(CmmData _ _)
+ = cmm
+
+natCmmTopToLive (CmmProc info lbl params (ListGraph []))
+ = CmmProc (LiveInfo info Nothing emptyBlockEnv)
+ lbl params (ListGraph []))
+
+natCmmTopToLive (CmmProc info lbl params (ListGraph blocks))
+ = let first_id = blockId first
+ sccs = sccBlocks blocks
+
+ liveBlocks
+ = map (\scc -> case scc of
+ AcyclicSCC b@(BasicBlock l _) -> BasicBlock l [cmmBlockToLive b]
+ CyclicSCC bs@(BasicBlock l _ : _) -> BasicBlock l (map cmmBlockToLive bs)
+ CyclicSCC []
+ -> panic "RegLiveNess.natCmmTopToLive: no blocks in scc list")
+ sccs
+
+ in CmmProc (LiveInfo info (Just first_id) ???
+-}
---------------------------------------------------------------------------------
-- Annotate code with register liveness information
--
regLiveness
- :: NatCmmTop
- -> UniqSM LiveCmmTop
+ :: Instruction instr
+ => NatCmmTop instr
+ -> UniqSM (LiveCmmTop instr)
regLiveness (CmmData i d)
= returnUs $ CmmData i d
regLiveness (CmmProc info lbl params (ListGraph []))
= returnUs $ CmmProc
- (LiveInfo info Nothing emptyBlockEnv)
- lbl params (ListGraph [])
+ (LiveInfo info Nothing (Just emptyBlockEnv))
+ lbl params []
regLiveness (CmmProc info lbl params (ListGraph blocks@(first : _)))
= let first_id = blockId first
sccs = sccBlocks blocks
(ann_sccs, block_live) = computeLiveness sccs
- liveBlocks
- = map (\scc -> case scc of
- AcyclicSCC b@(BasicBlock l _) -> BasicBlock l [b]
- CyclicSCC bs@(BasicBlock l _ : _) -> BasicBlock l bs
- CyclicSCC []
- -> panic "RegLiveness.regLiveness: no blocks in scc list")
- $ ann_sccs
+ in returnUs $ CmmProc (LiveInfo info (Just first_id) (Just block_live))
+ lbl params ann_sccs
- in returnUs $ CmmProc (LiveInfo info (Just first_id) block_live)
- lbl params (ListGraph liveBlocks)
+sccBlocks
+ :: Instruction instr
+ => [NatBasicBlock instr]
+ -> [SCC (NatBasicBlock instr)]
-sccBlocks :: [NatBasicBlock] -> [SCC NatBasicBlock]
sccBlocks blocks = stronglyConnCompFromEdgedVertices graph
where
- getOutEdges :: [Instr] -> [BlockId]
- getOutEdges instrs = foldl' (\a x -> jumpDests x a) [] instrs
+ getOutEdges :: Instruction instr => [instr] -> [BlockId]
+ getOutEdges instrs = concat $ map jumpDestsOfInstr instrs
graph = [ (block, getUnique id, map getUnique (getOutEdges instrs))
| block@(BasicBlock id instrs) <- blocks ]
-- Computing liveness
computeLiveness
- :: [SCC NatBasicBlock]
- -> ([SCC LiveBasicBlock], -- instructions annotated with list of registers
- -- which are "dead after this instruction".
- BlockMap RegSet) -- blocks annontated with set of live registers
- -- on entry to the block.
-
+ :: Instruction instr
+ => [SCC (NatBasicBlock instr)]
+ -> ([SCC (LiveBasicBlock instr)], -- instructions annotated with list of registers
+ -- which are "dead after this instruction".
+ BlockMap RegSet) -- blocks annontated with set of live registers
+ -- on entry to the block.
+
-- NOTE: on entry, the SCCs are in "reverse" order: later blocks may transfer
-- control to earlier ones only. The SCCs returned are in the *opposite*
-- order, which is exactly what we want for the next pass.
livenessSCCs
- :: BlockMap RegSet
- -> [SCC LiveBasicBlock] -- accum
- -> [SCC NatBasicBlock]
- -> ([SCC LiveBasicBlock], BlockMap RegSet)
+ :: Instruction instr
+ => BlockMap RegSet
+ -> [SCC (LiveBasicBlock instr)] -- accum
+ -> [SCC (NatBasicBlock instr)]
+ -> ( [SCC (LiveBasicBlock instr)]
+ , BlockMap RegSet)
livenessSCCs blockmap done [] = (done, blockmap)
(a, panic "RegLiveness.livenessSCCs")
- linearLiveness :: BlockMap RegSet -> [NatBasicBlock]
- -> (BlockMap RegSet, [LiveBasicBlock])
+ linearLiveness
+ :: Instruction instr
+ => BlockMap RegSet -> [NatBasicBlock instr]
+ -> (BlockMap RegSet, [LiveBasicBlock instr])
+
linearLiveness = mapAccumL livenessBlock
-- probably the least efficient way to compare two
-- | Annotate a basic block with register liveness information.
--
livenessBlock
- :: BlockMap RegSet
- -> NatBasicBlock
- -> (BlockMap RegSet, LiveBasicBlock)
+ :: Instruction instr
+ => BlockMap RegSet
+ -> NatBasicBlock instr
+ -> (BlockMap RegSet, LiveBasicBlock instr)
livenessBlock blockmap (BasicBlock block_id instrs)
= let
-- filling in when regs are born
livenessForward
- :: RegSet -- regs live on this instr
- -> [LiveInstr] -> [LiveInstr]
+ :: Instruction instr
+ => RegSet -- regs live on this instr
+ -> [LiveInstr instr] -> [LiveInstr instr]
livenessForward _ [] = []
livenessForward rsLiveEntry (li@(Instr instr mLive) : lis)
= li : livenessForward rsLiveEntry lis
| Just live <- mLive
- , RU _ written <- regUsage instr
+ , RU _ written <- regUsageOfInstr instr
= let
-- Regs that are written to but weren't live on entry to this instruction
-- are recorded as being born here.
-- filling in when regs die, and what regs are live across each instruction
livenessBack
- :: RegSet -- regs live on this instr
+ :: Instruction instr
+ => RegSet -- regs live on this instr
-> BlockMap RegSet -- regs live on entry to other BBs
- -> [LiveInstr] -- instructions (accum)
- -> [Instr] -- instructions
- -> (RegSet, [LiveInstr])
+ -> [LiveInstr instr] -- instructions (accum)
+ -> [instr] -- instructions
+ -> (RegSet, [LiveInstr instr])
livenessBack liveregs _ done [] = (liveregs, done)
= let (liveregs', instr') = liveness1 liveregs blockmap instr
in livenessBack liveregs' blockmap (instr' : acc) instrs
--- don't bother tagging comments or deltas with liveness
-liveness1 :: RegSet -> BlockMap RegSet -> Instr -> (RegSet, LiveInstr)
-liveness1 liveregs _ (instr@COMMENT{})
- = (liveregs, Instr instr Nothing)
-liveness1 liveregs _ (instr@DELTA{})
+-- don't bother tagging comments or deltas with liveness
+liveness1
+ :: Instruction instr
+ => RegSet
+ -> BlockMap RegSet
+ -> instr
+ -> (RegSet, LiveInstr instr)
+
+liveness1 liveregs _ instr
+ | isMetaInstr instr
= (liveregs, Instr instr Nothing)
liveness1 liveregs blockmap instr
- | not_a_branch
- = (liveregs1, Instr instr
+ | not_a_branch
+ = (liveregs1, Instr instr
(Just $ Liveness
{ liveBorn = emptyUniqSet
, liveDieRead = mkUniqSet r_dying
, liveDieWrite = mkUniqSet w_dying }))
- | otherwise
- = (liveregs_br, Instr instr
+ | otherwise
+ = (liveregs_br, Instr instr
(Just $ Liveness
{ liveBorn = emptyUniqSet
, liveDieRead = mkUniqSet r_dying_br
, liveDieWrite = mkUniqSet w_dying }))
- where
- RU read written = regUsage instr
+ where
+ RU read written = regUsageOfInstr instr
-- registers that were written here are dead going backwards.
-- registers that were read here are live going backwards.
-- union in the live regs from all the jump destinations of this
-- instruction.
- targets = jumpDests instr [] -- where we go from here
+ targets = jumpDestsOfInstr instr -- where we go from here
not_a_branch = null targets
targetLiveRegs target
live_branch_only)
-
-