module CmmSpillReload
( dualLivenessWithInsertion
)
where
import BlockId
import Cmm
import CmmUtils
import CmmLive
import OptimizationFuel
import Control.Monad
import Outputable hiding (empty)
import qualified Outputable as PP
import UniqSet
import Compiler.Hoopl hiding (Unique)
import Data.Maybe
import Prelude hiding (succ, zip)
data DualLive = DualLive { on_stack :: RegSet, in_regs :: RegSet }
changeStack, changeRegs :: (RegSet -> RegSet) -> DualLive -> DualLive
changeStack f live = live { on_stack = f (on_stack live) }
changeRegs f live = live { in_regs = f (in_regs live) }
dualLiveLattice :: DataflowLattice DualLive
dualLiveLattice = DataflowLattice "variables live in registers and on stack" empty add
where empty = DualLive emptyRegSet emptyRegSet
add _ (OldFact old) (NewFact new) = (changeIf $ change1 || change2, DualLive stack regs)
where (change1, stack) = add1 (on_stack old) (on_stack new)
(change2, regs) = add1 (in_regs old) (in_regs new)
add1 old new = if sizeUniqSet join > sizeUniqSet old then (True, join) else (False, old)
where join = unionUniqSets old new
dualLivenessWithInsertion :: BlockSet -> CmmGraph -> FuelUniqSM CmmGraph
dualLivenessWithInsertion procPoints g =
liftM fst $ dataflowPassBwd g [] $ analRewBwd dualLiveLattice
(dualLiveTransfers (g_entry g) procPoints)
(insertSpillsAndReloads g procPoints)
dualLiveTransfers :: BlockId -> BlockSet -> (BwdTransfer CmmNode DualLive)
dualLiveTransfers entry procPoints = mkBTransfer3 first middle last
where first :: CmmNode C O -> DualLive -> DualLive
first (CmmEntry id) live
| id == entry || setMember id procPoints = noLiveOnEntry id (in_regs live) live
| otherwise = live
middle :: CmmNode O O -> DualLive -> DualLive
middle m = changeStack updSlots
. changeRegs updRegs
where
updRegs = case getBTransfer3 xferLive of (_, middle, _) -> middle m
updSlots live = foldSlotsUsed reload (foldSlotsDefd spill live m) m
spill live s@(RegSlot r, _, _) = check s $ deleteFromRegSet live r
spill live _ = live
reload live s@(RegSlot r, _, _) = check s $ extendRegSet live r
reload live _ = live
check (RegSlot (LocalReg _ ty), o, w) x
| o == w && w == widthInBytes (typeWidth ty) = x
check _ _ = panic "dualLiveTransfers: slices unsupported"
last :: CmmNode O C -> FactBase DualLive -> DualLive
last l fb = changeRegs (gen_kill l) $ case l of
CmmCall {cml_cont=Nothing} -> empty
CmmCall {cml_cont=Just k} -> keep_stack_only k
CmmForeignCall {succ=k} -> keep_stack_only k
_ -> joinOutFacts dualLiveLattice l fb
where empty = fact_bot dualLiveLattice
lkp k = fromMaybe empty (lookupFact k fb)
keep_stack_only k = DualLive (on_stack (lkp k)) emptyRegSet
insertSpillsAndReloads :: CmmGraph -> BlockSet -> CmmBwdRewrite DualLive
insertSpillsAndReloads graph procPoints = deepBwdRw3 first middle nothing
where first :: CmmNode C O -> Fact O DualLive -> CmmReplGraph C O
first e@(CmmEntry id) live = return $
if id /= (g_entry graph) && setMember id procPoints then
case map reload (uniqSetToList (in_regs live)) of
[] -> Nothing
is -> Just $ mkFirst e <*> mkMiddles is
else Nothing
middle :: CmmNode O O -> Fact O DualLive -> CmmReplGraph O O
middle (CmmAssign (CmmLocal reg) (CmmLoad (CmmStackSlot (RegSlot reg') _) _)) _ | reg == reg' = return Nothing
middle m@(CmmAssign (CmmLocal reg) _) live
| reg `elemRegSet` on_stack live = return (Just (mkMiddles [m, spill reg]))
middle _ _ = return Nothing
nothing _ _ = return Nothing
spill, reload :: LocalReg -> CmmNode O O
spill r = CmmStore (regSlot r) (CmmReg $ CmmLocal r)
reload r = CmmAssign (CmmLocal r) (CmmLoad (regSlot r) $ localRegType r)
ppr_regs :: String -> RegSet -> SDoc
ppr_regs s regs = text s <+> commafy (map ppr $ uniqSetToList regs)
where commafy xs = hsep $ punctuate comma xs
instance Outputable DualLive where
ppr (DualLive {in_regs = regs, on_stack = stack}) =
if isEmptyUniqSet regs && isEmptyUniqSet stack then
text "<nothing-live>"
else
nest 2 $ fsep [if isEmptyUniqSet regs then PP.empty
else (ppr_regs "live in regs =" regs),
if isEmptyUniqSet stack then PP.empty
else (ppr_regs "live on stack =" stack)]