module CmmSink (
cmmSink,
cmmPeepholeInline
) where
import Cmm
import BlockId
import CmmLive
import CmmUtils
import Hoopl
import UniqFM
import Unique
import Outputable
import qualified Data.Set as Set
cmmSink :: CmmGraph -> CmmGraph
cmmSink graph = cmmSink' (cmmLiveness graph) graph
type Assignment = (LocalReg, CmmExpr, AbsAddr)
cmmSink' :: BlockEnv CmmLive -> CmmGraph -> CmmGraph
cmmSink' liveness graph
= ofBlockList (g_entry graph) $ sink mapEmpty $ postorderDfs graph
where
sink :: BlockEnv [Assignment] -> [CmmBlock] -> [CmmBlock]
sink _ [] = []
sink sunk (b:bs) =
pprTrace "sink" (ppr lbl) $
blockJoin first final_middle last : sink sunk' bs
where
lbl = entryLabel b
(first, middle, last) = blockSplit b
(middle', assigs) = walk (blockToList middle) emptyBlock
(mapFindWithDefault [] lbl sunk)
getLive l = mapFindWithDefault Set.empty l liveness
lives = map getLive (successors last)
multilive = [ r | (r,n) <- ufmToList livemap, n > 1 ]
where livemap = foldr (\r m -> addToUFM_C (+) m r (1::Int))
emptyUFM (concatMap Set.toList lives)
(dropped_last, assigs') = dropAssignments drop_if assigs
drop_if a@(r,_,_) = a `conflicts` last || getUnique r `elem` multilive
final_middle = foldl blockSnoc middle' dropped_last
sunk' = mapUnion sunk $
mapFromList [ (l, filterAssignments (getLive l) assigs')
| l <- successors last ]
filterAssignments :: RegSet -> [Assignment] -> [Assignment]
filterAssignments live assigs = reverse (go assigs [])
where go [] kept = kept
go (a@(r,_,_):as) kept | needed = go as (a:kept)
| otherwise = go as kept
where
needed = r `Set.member` live || any (a `conflicts`) (map toNode kept)
walk :: [CmmNode O O] -> Block CmmNode O O -> [Assignment]
-> (Block CmmNode O O, [Assignment])
walk [] block as = (block, as)
walk (n:ns) block as
| Just a <- shouldSink n = walk ns block (a : as)
| otherwise = walk ns block' as'
where
(dropped, as') = dropAssignments (`conflicts` n) as
block' = foldl blockSnoc block dropped `blockSnoc` n
shouldSink :: CmmNode O O -> Maybe Assignment
shouldSink (CmmAssign (CmmLocal r) e) | no_local_regs = Just (r, e, exprAddr e)
where no_local_regs = foldRegsUsed (\_ _ -> False) True e
shouldSink _other = Nothing
toNode :: Assignment -> CmmNode O O
toNode (r,rhs,_) = CmmAssign (CmmLocal r) rhs
dropAssignments :: (Assignment -> Bool) -> [Assignment] -> ([CmmNode O O], [Assignment])
dropAssignments should_drop assigs
= (dropped, reverse kept)
where
(dropped,kept) = go assigs [] []
go [] dropped kept = (dropped, kept)
go (assig : rest) dropped kept
| conflict = go rest (toNode assig : dropped) kept
| otherwise = go rest dropped (assig:kept)
where
conflict = should_drop assig || any (assig `conflicts`) dropped
conflicts :: Assignment -> CmmNode O x -> Bool
(_, rhs, _ ) `conflicts` CmmAssign reg _ | reg `regUsedIn` rhs = True
(_, _, addr) `conflicts` CmmStore addr' _ | addrConflicts addr (loadAddr addr') = True
(r, _, _) `conflicts` node
= foldRegsUsed (\b r' -> r == r' || b) False node
data AbsAddr = NoAddr | HeapAddr | StackAddr | AnyAddr
bothAddrs :: AbsAddr -> AbsAddr -> AbsAddr
bothAddrs NoAddr x = x
bothAddrs x NoAddr = x
bothAddrs HeapAddr HeapAddr = HeapAddr
bothAddrs StackAddr StackAddr = StackAddr
bothAddrs _ _ = AnyAddr
addrConflicts :: AbsAddr -> AbsAddr -> Bool
addrConflicts NoAddr _ = False
addrConflicts _ NoAddr = False
addrConflicts HeapAddr StackAddr = False
addrConflicts StackAddr HeapAddr = False
addrConflicts _ _ = True
exprAddr :: CmmExpr -> AbsAddr
exprAddr (CmmLoad addr _) = loadAddr addr
exprAddr (CmmMachOp _ es) = foldr bothAddrs NoAddr (map exprAddr es)
exprAddr _ = NoAddr
absAddr :: CmmExpr -> AbsAddr
absAddr (CmmLoad addr _) = bothAddrs HeapAddr (loadAddr addr)
absAddr (CmmMachOp _ es) = foldr bothAddrs NoAddr (map absAddr es)
absAddr (CmmReg r) = regAddr r
absAddr (CmmRegOff r _) = regAddr r
absAddr _ = NoAddr
loadAddr :: CmmExpr -> AbsAddr
loadAddr e = case absAddr e of
NoAddr -> HeapAddr
a -> a
regAddr :: CmmReg -> AbsAddr
regAddr (CmmGlobal Sp) = StackAddr
regAddr (CmmGlobal Hp) = HeapAddr
regAddr _ = NoAddr
cmmPeepholeInline :: CmmGraph -> CmmGraph
cmmPeepholeInline graph = ofBlockList (g_entry graph) $ map do_block (toBlockList graph)
where
liveness = cmmLiveness graph
do_block :: Block CmmNode C C -> Block CmmNode C C
do_block block = blockJoin first (go rmiddle live_middle) last
where
(first, middle, last) = blockSplit block
rmiddle = reverse (blockToList middle)
live = Set.unions [ mapFindWithDefault Set.empty l liveness | l <- successors last ]
live_middle = gen_kill last live
go :: [CmmNode O O] -> RegSet -> Block CmmNode O O
go [] _ = emptyBlock
go [stmt] _ = blockCons stmt emptyBlock
go (stmt : rest) live = tryInline stmt usages live rest
where
usages :: UniqFM Int
usages = foldRegsUsed addUsage emptyUFM stmt
addUsage :: UniqFM Int -> LocalReg -> UniqFM Int
addUsage m r = addToUFM_C (+) m r 1
tryInline stmt usages live (CmmAssign (CmmLocal l) rhs : rest)
| not (l `elemRegSet` live),
Just 1 <- lookupUFM usages l = tryInline stmt' usages' live' rest
where live' = foldRegsUsed extendRegSet live rhs
usages' = foldRegsUsed addUsage usages rhs
stmt' = mapExpDeep inline stmt
where inline (CmmReg (CmmLocal l')) | l == l' = rhs
inline (CmmRegOff (CmmLocal l') off) | l == l' = cmmOffset rhs off
inline other = other
tryInline stmt _usages live stmts
= go stmts (gen_kill stmt live) `blockSnoc` stmt