module GHC.CmmToAsm.Reg.Graph.SpillClean (
cleanSpills
) where
import GHC.Prelude
import GHC.CmmToAsm.Reg.Liveness
import GHC.CmmToAsm.Instr
import GHC.Platform.Reg
import GHC.Cmm.BlockId
import GHC.Cmm
import GHC.Types.Unique.Set
import GHC.Types.Unique.FM
import GHC.Types.Unique
import GHC.Builtin.Uniques
import GHC.Utils.Monad.State
import GHC.Utils.Outputable
import GHC.Utils.Panic
import GHC.Platform
import GHC.Cmm.Dataflow.Collections
import Data.List (nub, foldl1', find)
import Data.Maybe
import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet
type Slot = Int
cleanSpills
:: Instruction instr
=> Platform
-> LiveCmmDecl statics instr
-> LiveCmmDecl statics instr
cleanSpills platform cmm
= evalState (cleanSpin platform 0 cmm) initCleanS
cleanSpin
:: Instruction instr
=> Platform
-> Int
-> LiveCmmDecl statics instr
-> CleanM (LiveCmmDecl statics instr)
cleanSpin platform spinCount code
= do
modify $ \s -> s
{ sCleanedSpillsAcc = 0
, sCleanedReloadsAcc = 0
, sReloadedBy = emptyUFM }
code_forward <- mapBlockTopM (cleanBlockForward platform) code
code_backward <- cleanTopBackward code_forward
collateJoinPoints
spills <- gets sCleanedSpillsAcc
reloads <- gets sCleanedReloadsAcc
modify $ \s -> s
{ sCleanedCount = (spills, reloads) : sCleanedCount s }
cleanedCount <- gets sCleanedCount
if take 2 cleanedCount == [(0, 0), (0, 0)]
then return code
else cleanSpin platform (spinCount + 1) code_backward
cleanBlockForward
:: Instruction instr
=> Platform
-> LiveBasicBlock instr
-> CleanM (LiveBasicBlock instr)
cleanBlockForward platform (BasicBlock blockId instrs)
= do
jumpValid <- gets sJumpValid
let assoc = case lookupUFM jumpValid blockId of
Just assoc -> assoc
Nothing -> emptyAssoc
instrs_reload <- cleanForward platform blockId assoc [] instrs
return $ BasicBlock blockId instrs_reload
cleanForward
:: Instruction instr
=> Platform
-> BlockId
-> Assoc Store
-> [LiveInstr instr]
-> [LiveInstr instr]
-> CleanM [LiveInstr instr]
cleanForward _ _ _ acc []
= return acc
cleanForward platform blockId assoc acc (li1 : li2 : instrs)
| LiveInstr (SPILL reg1 slot1) _ <- li1
, LiveInstr (RELOAD slot2 reg2) _ <- li2
, slot1 == slot2
= do
modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
cleanForward platform blockId assoc acc
$ li1 : LiveInstr (mkRegRegMoveInstr platform reg1 reg2) Nothing
: instrs
cleanForward platform blockId assoc acc (li@(LiveInstr i1 _) : instrs)
| Just (r1, r2) <- takeRegRegMoveInstr i1
= if r1 == r2
then cleanForward platform blockId assoc acc instrs
else do let assoc' = addAssoc (SReg r1) (SReg r2)
$ delAssoc (SReg r2)
$ assoc
cleanForward platform blockId assoc' (li : acc) instrs
cleanForward platform blockId assoc acc (li : instrs)
| LiveInstr (SPILL reg slot) _ <- li
= let assoc' = addAssoc (SReg reg) (SSlot slot)
$ delAssoc (SSlot slot)
$ assoc
in cleanForward platform blockId assoc' (li : acc) instrs
| LiveInstr (RELOAD{}) _ <- li
= do (assoc', mli) <- cleanReload platform blockId assoc li
case mli of
Nothing -> cleanForward platform blockId assoc' acc
instrs
Just li' -> cleanForward platform blockId assoc' (li' : acc)
instrs
| LiveInstr instr _ <- li
, targets <- jumpDestsOfInstr instr
, not $ null targets
= do mapM_ (accJumpValid assoc) targets
cleanForward platform blockId assoc (li : acc) instrs
| LiveInstr instr _ <- li
, RU _ written <- regUsageOfInstr platform instr
= let assoc' = foldr delAssoc assoc (map SReg $ nub written)
in cleanForward platform blockId assoc' (li : acc) instrs
cleanReload
:: Instruction instr
=> Platform
-> BlockId
-> Assoc Store
-> LiveInstr instr
-> CleanM (Assoc Store, Maybe (LiveInstr instr))
cleanReload platform blockId assoc li@(LiveInstr (RELOAD slot reg) _)
| elemAssoc (SSlot slot) (SReg reg) assoc
= do modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
return (assoc, Nothing)
| Just reg2 <- findRegOfSlot assoc slot
= do modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
let assoc' = addAssoc (SReg reg) (SReg reg2)
$ delAssoc (SReg reg)
$ assoc
return ( assoc'
, Just $ LiveInstr (mkRegRegMoveInstr platform reg2 reg) Nothing)
| otherwise
= do
let assoc'
= addAssoc (SReg reg) (SSlot slot)
$ delAssoc (SReg reg)
$ assoc
accBlockReloadsSlot blockId slot
return (assoc', Just li)
cleanReload _ _ _ _
= panic "RegSpillClean.cleanReload: unhandled instr"
cleanTopBackward
:: Instruction instr
=> LiveCmmDecl statics instr
-> CleanM (LiveCmmDecl statics instr)
cleanTopBackward cmm
= case cmm of
CmmData{}
-> return cmm
CmmProc info label live sccs
| LiveInfo _ _ _ liveSlotsOnEntry <- info
-> do sccs' <- mapM (mapSCCM (cleanBlockBackward liveSlotsOnEntry)) sccs
return $ CmmProc info label live sccs'
cleanBlockBackward
:: Instruction instr
=> BlockMap IntSet
-> LiveBasicBlock instr
-> CleanM (LiveBasicBlock instr)
cleanBlockBackward liveSlotsOnEntry (BasicBlock blockId instrs)
= do instrs_spill <- cleanBackward liveSlotsOnEntry emptyUniqSet [] instrs
return $ BasicBlock blockId instrs_spill
cleanBackward
:: Instruction instr
=> BlockMap IntSet
-> UniqSet Int
-> [LiveInstr instr]
-> [LiveInstr instr]
-> CleanM [LiveInstr instr]
cleanBackward liveSlotsOnEntry noReloads acc lis
= do reloadedBy <- gets sReloadedBy
cleanBackward' liveSlotsOnEntry reloadedBy noReloads acc lis
cleanBackward'
:: Instruction instr
=> BlockMap IntSet
-> UniqFM Store [BlockId]
-> UniqSet Int
-> [LiveInstr instr]
-> [LiveInstr instr]
-> State CleanS [LiveInstr instr]
cleanBackward' _ _ _ acc []
= return acc
cleanBackward' liveSlotsOnEntry reloadedBy noReloads acc (li : instrs)
| LiveInstr (SPILL _ slot) _ <- li
, Nothing <- lookupUFM reloadedBy (SSlot slot)
= do modify $ \s -> s { sCleanedSpillsAcc = sCleanedSpillsAcc s + 1 }
cleanBackward liveSlotsOnEntry noReloads acc instrs
| LiveInstr (SPILL _ slot) _ <- li
= if elementOfUniqSet slot noReloads
then do
modify $ \s -> s { sCleanedSpillsAcc = sCleanedSpillsAcc s + 1 }
cleanBackward liveSlotsOnEntry noReloads acc instrs
else do
let noReloads' = addOneToUniqSet noReloads slot
cleanBackward liveSlotsOnEntry noReloads' (li : acc) instrs
| LiveInstr (RELOAD slot _) _ <- li
, noReloads' <- delOneFromUniqSet noReloads slot
= cleanBackward liveSlotsOnEntry noReloads' (li : acc) instrs
| LiveInstr instr _ <- li
, targets <- jumpDestsOfInstr instr
= do
let slotsReloadedByTargets
= IntSet.unions
$ catMaybes
$ map (flip mapLookup liveSlotsOnEntry)
$ targets
let noReloads'
= foldl' delOneFromUniqSet noReloads
$ IntSet.toList slotsReloadedByTargets
cleanBackward liveSlotsOnEntry noReloads' (li : acc) instrs
#if __GLASGOW_HASKELL__ <= 810
| otherwise
= cleanBackward liveSlotsOnEntry noReloads (li : acc) instrs
#endif
collateJoinPoints :: CleanM ()
collateJoinPoints
= modify $ \s -> s
{ sJumpValid = mapUFM intersects (sJumpValidAcc s)
, sJumpValidAcc = emptyUFM }
intersects :: [Assoc Store] -> Assoc Store
intersects [] = emptyAssoc
intersects assocs = foldl1' intersectAssoc assocs
findRegOfSlot :: Assoc Store -> Int -> Maybe Reg
findRegOfSlot assoc slot
| close <- closeAssoc (SSlot slot) assoc
, Just (SReg reg) <- find isStoreReg $ nonDetEltsUniqSet close
= Just reg
| otherwise
= Nothing
type CleanM
= State CleanS
data CleanS
= CleanS
{
sJumpValid :: UniqFM BlockId (Assoc Store)
, sJumpValidAcc :: UniqFM BlockId [Assoc Store]
, sReloadedBy :: UniqFM Store [BlockId]
, sCleanedCount :: [(Int, Int)]
, sCleanedSpillsAcc :: Int
, sCleanedReloadsAcc :: Int }
initCleanS :: CleanS
initCleanS
= CleanS
{ sJumpValid = emptyUFM
, sJumpValidAcc = emptyUFM
, sReloadedBy = emptyUFM
, sCleanedCount = []
, sCleanedSpillsAcc = 0
, sCleanedReloadsAcc = 0 }
accJumpValid :: Assoc Store -> BlockId -> CleanM ()
accJumpValid assocs target
= modify $ \s -> s {
sJumpValidAcc = addToUFM_C (++)
(sJumpValidAcc s)
target
[assocs] }
accBlockReloadsSlot :: BlockId -> Slot -> CleanM ()
accBlockReloadsSlot blockId slot
= modify $ \s -> s {
sReloadedBy = addToUFM_C (++)
(sReloadedBy s)
(SSlot slot)
[blockId] }
data Store
= SSlot Int
| SReg Reg
isStoreReg :: Store -> Bool
isStoreReg ss
= case ss of
SSlot _ -> False
SReg _ -> True
instance Uniquable Store where
getUnique (SReg r)
| RegReal (RealRegSingle i) <- r
= mkRegSingleUnique i
| RegReal (RealRegPair r1 r2) <- r
= mkRegPairUnique (r1 * 65535 + r2)
| otherwise
= error $ "RegSpillClean.getUnique: found virtual reg during spill clean,"
++ "only real regs expected."
getUnique (SSlot i) = mkRegSubUnique i
instance Outputable Store where
ppr (SSlot i) = text "slot" <> int i
ppr (SReg r) = ppr r
type Assoc a = UniqFM a (UniqSet a)
emptyAssoc :: Assoc a
emptyAssoc = emptyUFM
addAssoc :: Store -> Store -> Assoc Store -> Assoc Store
addAssoc a b m
= let m1 = addToUFM_C unionUniqSets m a (unitUniqSet b)
m2 = addToUFM_C unionUniqSets m1 b (unitUniqSet a)
in m2
delAssoc :: Store -> Assoc Store -> Assoc Store
delAssoc a m
| Just aSet <- lookupUFM m a
, m1 <- delFromUFM m a
= nonDetStrictFoldUniqSet (\x m -> delAssoc1 x a m) m1 aSet
| otherwise = m
delAssoc1 :: Store -> Store -> Assoc Store -> Assoc Store
delAssoc1 a b m
| Just aSet <- lookupUFM m a
= addToUFM m a (delOneFromUniqSet aSet b)
| otherwise = m
elemAssoc :: Store -> Store -> Assoc Store -> Bool
elemAssoc a b m
= elementOfUniqSet b (closeAssoc a m)
closeAssoc :: Store -> Assoc Store -> UniqSet Store
closeAssoc a assoc
= closeAssoc' assoc emptyUniqSet (unitUniqSet a)
where
closeAssoc' assoc visited toVisit
= case nonDetEltsUniqSet toVisit of
[] -> visited
(x:_)
| elementOfUniqSet x visited
-> closeAssoc' assoc visited (delOneFromUniqSet toVisit x)
| otherwise
-> let neighbors
= case lookupUFM assoc x of
Nothing -> emptyUniqSet
Just set -> set
in closeAssoc' assoc
(addOneToUniqSet visited x)
(unionUniqSets toVisit neighbors)
intersectAssoc :: Assoc Store -> Assoc Store -> Assoc Store
intersectAssoc a b
= intersectUFM_C (intersectUniqSets) a b