module GHC.CmmToAsm.Reg.Graph.Spill (
regSpill,
SpillStats(..),
accSpillSL
) where
import GHC.Prelude
import GHC.CmmToAsm.Reg.Liveness
import GHC.CmmToAsm.Reg.Utils
import GHC.CmmToAsm.Instr
import GHC.Platform.Reg
import GHC.Cmm hiding (RegSet)
import GHC.Cmm.BlockId
import GHC.Cmm.Dataflow.Collections
import GHC.Utils.Monad
import GHC.Utils.Monad.State
import GHC.Types.Unique
import GHC.Types.Unique.FM
import GHC.Types.Unique.Set
import GHC.Types.Unique.Supply
import GHC.Utils.Outputable
import GHC.Utils.Panic
import GHC.Platform
import Data.List (nub, (\\), intersect)
import Data.Maybe
import Data.IntSet (IntSet)
import qualified Data.IntSet as IntSet
regSpill
:: Instruction instr
=> Platform
-> [LiveCmmDecl statics instr]
-> UniqSet Int
-> Int
-> UniqSet VirtualReg
-> UniqSM
([LiveCmmDecl statics instr]
, UniqSet Int
, Int
, SpillStats )
regSpill platform code slotsFree slotCount regs
| sizeUniqSet slotsFree < sizeUniqSet regs
=
let slotsFree' = (addListToUniqSet slotsFree [slotCount+1 .. slotCount+512])
in regSpill platform code slotsFree' (slotCount+512) regs
| otherwise
= do
let slots = take (sizeUniqSet regs) $ nonDetEltsUniqSet slotsFree
let
regSlotMap = toRegMap
$ listToUFM
$ zip (nonDetEltsUniqSet regs) slots :: UniqFM Reg Int
us <- getUniqueSupplyM
let (code', state') =
runState (mapM (regSpill_top platform regSlotMap) code)
(initSpillS us)
return ( code'
, minusUniqSet slotsFree (mkUniqSet slots)
, slotCount
, makeSpillStats state')
regSpill_top
:: Instruction instr
=> Platform
-> RegMap Int
-> LiveCmmDecl statics instr
-> SpillM (LiveCmmDecl statics instr)
regSpill_top platform regSlotMap cmm
= case cmm of
CmmData{}
-> return cmm
CmmProc info label live sccs
| LiveInfo static firstId liveVRegsOnEntry liveSlotsOnEntry <- info
-> do
let liveSlotsOnEntry' :: BlockMap IntSet
liveSlotsOnEntry'
= mapFoldlWithKey patchLiveSlot
liveSlotsOnEntry liveVRegsOnEntry
let info'
= LiveInfo static firstId
liveVRegsOnEntry
liveSlotsOnEntry'
sccs' <- mapM (mapSCCM (regSpill_block platform regSlotMap)) sccs
return $ CmmProc info' label live sccs'
where
patchLiveSlot
:: BlockMap IntSet -> BlockId -> RegSet -> BlockMap IntSet
patchLiveSlot slotMap blockId regsLive
= let
curSlotsLive = fromMaybe IntSet.empty
$ mapLookup blockId slotMap
moreSlotsLive = IntSet.fromList
$ catMaybes
$ map (lookupUFM regSlotMap)
$ nonDetEltsUniqSet regsLive
slotMap'
= mapInsert blockId (IntSet.union curSlotsLive moreSlotsLive)
slotMap
in slotMap'
regSpill_block
:: Instruction instr
=> Platform
-> UniqFM Reg Int
-> LiveBasicBlock instr
-> SpillM (LiveBasicBlock instr)
regSpill_block platform regSlotMap (BasicBlock i instrs)
= do instrss' <- mapM (regSpill_instr platform regSlotMap) instrs
return $ BasicBlock i (concat instrss')
regSpill_instr
:: Instruction instr
=> Platform
-> UniqFM Reg Int
-> LiveInstr instr
-> SpillM [LiveInstr instr]
regSpill_instr _ _ li@(LiveInstr _ Nothing) = return [li]
regSpill_instr platform regSlotMap (LiveInstr instr (Just _)) = do
let RU rlRead rlWritten = regUsageOfInstr platform instr
let rsRead_ = nub rlRead
let rsWritten_ = nub rlWritten
let rsRead = rsRead_ \\ rsWritten_
let rsWritten = rsWritten_ \\ rsRead_
let rsModify = intersect rsRead_ rsWritten_
let rsSpillRead = filter (\r -> elemUFM r regSlotMap) rsRead
let rsSpillWritten = filter (\r -> elemUFM r regSlotMap) rsWritten
let rsSpillModify = filter (\r -> elemUFM r regSlotMap) rsModify
(instr1, prepost1) <- mapAccumLM (spillRead regSlotMap) instr rsSpillRead
(instr2, prepost2) <- mapAccumLM (spillWrite regSlotMap) instr1 rsSpillWritten
(instr3, prepost3) <- mapAccumLM (spillModify regSlotMap) instr2 rsSpillModify
let (mPrefixes, mPostfixes) = unzip (prepost1 ++ prepost2 ++ prepost3)
let prefixes = concat mPrefixes
let postfixes = concat mPostfixes
let instrs' = prefixes
++ [LiveInstr instr3 Nothing]
++ postfixes
return instrs'
spillRead
:: Instruction instr
=> UniqFM Reg Int
-> instr
-> Reg
-> SpillM (instr, ([LiveInstr instr'], [LiveInstr instr']))
spillRead regSlotMap instr reg
| Just slot <- lookupUFM regSlotMap reg
= do (instr', nReg) <- patchInstr reg instr
modify $ \s -> s
{ stateSpillSL = addToUFM_C accSpillSL (stateSpillSL s) reg (reg, 0, 1) }
return ( instr'
, ( [LiveInstr (RELOAD slot nReg) Nothing]
, []) )
| otherwise = panic "RegSpill.spillRead: no slot defined for spilled reg"
spillWrite
:: Instruction instr
=> UniqFM Reg Int
-> instr
-> Reg
-> SpillM (instr, ([LiveInstr instr'], [LiveInstr instr']))
spillWrite regSlotMap instr reg
| Just slot <- lookupUFM regSlotMap reg
= do (instr', nReg) <- patchInstr reg instr
modify $ \s -> s
{ stateSpillSL = addToUFM_C accSpillSL (stateSpillSL s) reg (reg, 1, 0) }
return ( instr'
, ( []
, [LiveInstr (SPILL nReg slot) Nothing]))
| otherwise = panic "RegSpill.spillWrite: no slot defined for spilled reg"
spillModify
:: Instruction instr
=> UniqFM Reg Int
-> instr
-> Reg
-> SpillM (instr, ([LiveInstr instr'], [LiveInstr instr']))
spillModify regSlotMap instr reg
| Just slot <- lookupUFM regSlotMap reg
= do (instr', nReg) <- patchInstr reg instr
modify $ \s -> s
{ stateSpillSL = addToUFM_C accSpillSL (stateSpillSL s) reg (reg, 1, 1) }
return ( instr'
, ( [LiveInstr (RELOAD slot nReg) Nothing]
, [LiveInstr (SPILL nReg slot) Nothing]))
| otherwise = panic "RegSpill.spillModify: no slot defined for spilled reg"
patchInstr
:: Instruction instr
=> Reg -> instr -> SpillM (instr, Reg)
patchInstr reg instr
= do nUnique <- newUnique
let nReg
= case reg of
RegVirtual vr
-> RegVirtual (renameVirtualReg nUnique vr)
RegReal{}
-> panic "RegAlloc.Graph.Spill.patchIntr: not patching real reg"
let instr' = patchReg1 reg nReg instr
return (instr', nReg)
patchReg1
:: Instruction instr
=> Reg -> Reg -> instr -> instr
patchReg1 old new instr
= let patchF r
| r == old = new
| otherwise = r
in patchRegsOfInstr instr patchF
type SpillM a
= State SpillS a
data SpillS
= SpillS
{
stateUS :: UniqSupply
, stateSpillSL :: UniqFM Reg (Reg, Int, Int) }
initSpillS :: UniqSupply -> SpillS
initSpillS uniqueSupply
= SpillS
{ stateUS = uniqueSupply
, stateSpillSL = emptyUFM }
newUnique :: SpillM Unique
newUnique
= do us <- gets stateUS
case takeUniqFromSupply us of
(uniq, us')
-> do modify $ \s -> s { stateUS = us' }
return uniq
accSpillSL :: (Reg, Int, Int) -> (Reg, Int, Int) -> (Reg, Int, Int)
accSpillSL (r1, s1, l1) (_, s2, l2)
= (r1, s1 + s2, l1 + l2)
data SpillStats
= SpillStats
{ spillStoreLoad :: UniqFM Reg (Reg, Int, Int) }
makeSpillStats :: SpillS -> SpillStats
makeSpillStats s
= SpillStats
{ spillStoreLoad = stateSpillSL s }
instance Outputable SpillStats where
ppr stats
= pprUFM (spillStoreLoad stats)
(vcat . map (\(r, s, l) -> ppr r <+> int s <+> int l))