summaryrefslogtreecommitdiff
path: root/compiler/nativeGen/RegAlloc
diff options
context:
space:
mode:
authorBen Lippmeier <benl@ouroborus.net>2012-11-12 15:10:51 +1100
committerBen Lippmeier <benl@ouroborus.net>2012-11-14 17:04:56 +1100
commitcace1caf905e0503176e93769238fbcec5283477 (patch)
treedaa28f77bb228fd66a9e25dc0127c618e861d9d6 /compiler/nativeGen/RegAlloc
parentb13ebb673871f06ef12c6358eecbcd8572a1a5f3 (diff)
downloadhaskell-cace1caf905e0503176e93769238fbcec5283477.tar.gz
Comments and formatting to spill cleaner
No functional changes.
Diffstat (limited to 'compiler/nativeGen/RegAlloc')
-rw-r--r--compiler/nativeGen/RegAlloc/Graph/SpillClean.hs313
1 files changed, 166 insertions, 147 deletions
diff --git a/compiler/nativeGen/RegAlloc/Graph/SpillClean.hs b/compiler/nativeGen/RegAlloc/Graph/SpillClean.hs
index ebef484aed..a81d76dd8d 100644
--- a/compiler/nativeGen/RegAlloc/Graph/SpillClean.hs
+++ b/compiler/nativeGen/RegAlloc/Graph/SpillClean.hs
@@ -1,6 +1,8 @@
--- | Clean out unneeded spill\/reload instrs
+
+-- | Clean out unneeded spill\/reload instructions.
--
--- * Handling of join points
+-- Handling of join points
+-- ~~~~~~~~~~~~~~~~~~~~~~~
--
-- B1: B2:
-- ... ...
@@ -12,21 +14,20 @@
-- RELOAD SLOT(0), %r1
-- ...
--
--- the plan:
--- So long as %r1 hasn't been written to in A, B or C then we don't need the
--- reload in B3.
+-- The Plan
+-- ~~~~~~~~
+-- As long as %r1 hasn't been written to in A, B or C then we don't need
+-- the reload in B3.
--
--- What we really care about here is that on the entry to B3, %r1 will always
--- have the same value that is in SLOT(0) (ie, %r1 is _valid_)
+-- What we really care about here is that on the entry to B3, %r1 will
+-- always have the same value that is in SLOT(0) (ie, %r1 is _valid_)
--
--- This also works if the reloads in B1\/B2 were spills instead, because
--- spilling %r1 to a slot makes that slot have the same value as %r1.
+-- This also works if the reloads in B1\/B2 were spills instead, because
+-- spilling %r1 to a slot makes that slot have the same value as %r1.
--
module RegAlloc.Graph.SpillClean (
cleanSpills
-)
-where
-
+) where
import RegAlloc.Liveness
import Instruction
import Reg
@@ -47,41 +48,35 @@ import Data.Set (Set)
import qualified Data.Map as Map
import qualified Data.Set as Set
---
+
+-- | The identification number of a spill slot.
+-- A value is stored in a spill slot when we don't have a free
+-- register to hold it.
type Slot = Int
-- | Clean out unneeded spill\/reloads from this top level thing.
cleanSpills
- :: Instruction instr
- => Platform -> LiveCmmDecl statics instr -> LiveCmmDecl statics instr
+ :: Instruction instr
+ => Platform
+ -> LiveCmmDecl statics instr
+ -> LiveCmmDecl statics instr
cleanSpills platform cmm
- = evalState (cleanSpin platform 0 cmm) initCleanS
+ = evalState (cleanSpin platform 0 cmm) initCleanS
--- | do one pass of cleaning
+
+-- | Do one pass of cleaning.
cleanSpin
- :: Instruction instr
- => Platform
- -> Int
- -> LiveCmmDecl statics instr
- -> CleanM (LiveCmmDecl statics instr)
-
-{-
-cleanSpin _ spinCount code
- = do jumpValid <- gets sJumpValid
- pprTrace "cleanSpin"
- ( int spinCount
- $$ text "--- code"
- $$ ppr code
- $$ text "--- joins"
- $$ ppr jumpValid)
- $ cleanSpin' spinCount code
--}
+ :: Instruction instr
+ => Platform
+ -> Int -- ^ Iteration number for the cleaner.
+ -> LiveCmmDecl statics instr -- ^ Liveness annotated code to clean.
+ -> CleanM (LiveCmmDecl statics instr)
cleanSpin platform spinCount code
= do
- -- init count of cleaned spills\/reloads
+ -- Initialise count of cleaned spill and reload instructions.
modify $ \s -> s
{ sCleanedSpillsAcc = 0
, sCleanedReloadsAcc = 0
@@ -90,19 +85,20 @@ cleanSpin platform spinCount code
code_forward <- mapBlockTopM (cleanBlockForward platform) code
code_backward <- cleanTopBackward code_forward
- -- During the cleaning of each block we collected information about what regs
- -- were valid across each jump. Based on this, work out whether it will be
- -- safe to erase reloads after join points for the next pass.
+ -- During the cleaning of each block we collected information about
+ -- what regs were valid across each jump. Based on this, work out
+ -- whether it will be safe to erase reloads after join points for
+ -- the next pass.
collateJoinPoints
- -- remember how many spills\/reloads we cleaned in this pass
+ -- Remember how many spill and reload instructions we cleaned in this pass.
spills <- gets sCleanedSpillsAcc
reloads <- gets sCleanedReloadsAcc
modify $ \s -> s
{ sCleanedCount = (spills, reloads) : sCleanedCount s }
- -- if nothing was cleaned in this pass or the last one
- -- then we're done and it's time to bail out
+ -- If nothing was cleaned in this pass or the last one
+ -- then we're done and it's time to bail out.
cleanedCount <- gets sCleanedCount
if take 2 cleanedCount == [(0, 0), (0, 0)]
then return code
@@ -111,16 +107,18 @@ cleanSpin platform spinCount code
else cleanSpin platform (spinCount + 1) code_backward
--- | Clean one basic block
+-------------------------------------------------------------------------------
+-- | Clean out unneeded reload instructions,
+-- while walking forward over the code.
cleanBlockForward
- :: Instruction instr
- => Platform
- -> LiveBasicBlock instr
- -> CleanM (LiveBasicBlock instr)
+ :: Instruction instr
+ => Platform
+ -> LiveBasicBlock instr
+ -> CleanM (LiveBasicBlock instr)
cleanBlockForward platform (BasicBlock blockId instrs)
= do
- -- see if we have a valid association for the entry to this block
+ -- See if we have a valid association for the entry to this block.
jumpValid <- gets sJumpValid
let assoc = case lookupUFM jumpValid blockId of
Just assoc -> assoc
@@ -132,25 +130,26 @@ cleanBlockForward platform (BasicBlock blockId instrs)
-- | Clean out unneeded reload instructions.
--- Walking forwards across the code
--- On a reload, if we know a reg already has the same value as a slot
--- then we don't need to do the reload.
+--
+-- Walking forwards across the code
+-- On a reload, if we know a reg already has the same value as a slot
+-- then we don't need to do the reload.
--
cleanForward
- :: Instruction instr
- => Platform
- -> BlockId -- ^ the block that we're currently in
- -> Assoc Store -- ^ two store locations are associated if they have the same value
- -> [LiveInstr instr] -- ^ acc
- -> [LiveInstr instr] -- ^ instrs to clean (in backwards order)
- -> CleanM [LiveInstr instr] -- ^ cleaned instrs (in forward order)
+ :: Instruction instr
+ => Platform
+ -> BlockId -- ^ the block that we're currently in
+ -> Assoc Store -- ^ two store locations are associated if
+ -- they have the same value
+ -> [LiveInstr instr] -- ^ acc
+ -> [LiveInstr instr] -- ^ instrs to clean (in backwards order)
+ -> CleanM [LiveInstr instr] -- ^ cleaned instrs (in forward order)
cleanForward _ _ _ acc []
= return acc
--- write out live range joins via spill slots to just a spill and a reg-reg move
--- hopefully the spill will be also be cleaned in the next pass
---
+-- Rewrite live range joins via spill slots to just a spill and a reg-reg move
+-- hopefully the spill will be also be cleaned in the next pass
cleanForward platform blockId assoc acc (li1 : li2 : instrs)
| LiveInstr (SPILL reg1 slot1) _ <- li1
@@ -159,18 +158,18 @@ cleanForward platform blockId assoc acc (li1 : li2 : instrs)
= do
modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
cleanForward platform blockId assoc acc
- (li1 : LiveInstr (mkRegRegMoveInstr platform reg1 reg2) Nothing : instrs)
-
+ $ 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
- -- erase any left over nop reg reg moves while we're here
- -- this will also catch any nop moves that the "write out live range joins" case above
- -- happens to add
+ -- Erase any left over nop reg reg moves while we're here
+ -- this will also catch any nop moves that the previous case
+ -- happens to add.
then cleanForward platform blockId assoc acc instrs
- -- if r1 has the same value as some slots and we copy r1 to r2,
+ -- If r1 has the same value as some slots and we copy r1 to r2,
-- then r2 is now associated with those slots instead
else do let assoc' = addAssoc (SReg r1) (SReg r2)
$ delAssoc (SReg r2)
@@ -181,28 +180,31 @@ cleanForward platform blockId assoc acc (li@(LiveInstr i1 _) : instrs)
cleanForward platform blockId assoc acc (li : instrs)
- -- update association due to the spill
+ -- Update association due to the spill.
| LiveInstr (SPILL reg slot) _ <- li
= let assoc' = addAssoc (SReg reg) (SSlot slot)
$ delAssoc (SSlot slot)
$ assoc
in cleanForward platform blockId assoc' (li : acc) instrs
- -- clean a reload instr
+ -- Clean a reload instr.
| 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
+ Nothing -> cleanForward platform blockId assoc' acc
+ instrs
+
+ Just li' -> cleanForward platform blockId assoc' (li' : acc)
+ instrs
- -- remember the association over a jump
+ -- Remember the association over a jump.
| LiveInstr instr _ <- li
, targets <- jumpDestsOfInstr instr
, not $ null targets
= do mapM_ (accJumpValid assoc) targets
cleanForward platform blockId assoc (li : acc) instrs
- -- writing to a reg changes its value.
+ -- Writing to a reg changes its value.
| LiveInstr instr _ <- li
, RU _ written <- regUsageOfInstr platform instr
= let assoc' = foldr delAssoc assoc (map SReg $ nub written)
@@ -211,24 +213,23 @@ cleanForward platform blockId assoc acc (li : instrs)
-- | Try and rewrite a reload instruction to something more pleasing
---
cleanReload
- :: Instruction instr
- => Platform
- -> BlockId
- -> Assoc Store
- -> LiveInstr instr
- -> CleanM (Assoc Store, Maybe (LiveInstr instr))
+ :: Instruction instr
+ => Platform
+ -> BlockId
+ -> Assoc Store
+ -> LiveInstr instr
+ -> CleanM (Assoc Store, Maybe (LiveInstr instr))
cleanReload platform blockId assoc li@(LiveInstr (RELOAD slot reg) _)
- -- if the reg we're reloading already has the same value as the slot
- -- then we can erase the instruction outright
+ -- If the reg we're reloading already has the same value as the slot
+ -- then we can erase the instruction outright.
| elemAssoc (SSlot slot) (SReg reg) assoc
= do modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
return (assoc, Nothing)
- -- if we can find another reg with the same value as this slot then
+ -- If we can find another reg with the same value as this slot then
-- do a move instead of a reload.
| Just reg2 <- findRegOfSlot assoc slot
= do modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
@@ -237,16 +238,20 @@ cleanReload platform blockId assoc li@(LiveInstr (RELOAD slot reg) _)
$ delAssoc (SReg reg)
$ assoc
- return (assoc', Just $ LiveInstr (mkRegRegMoveInstr platform reg2 reg) Nothing)
+ return ( assoc'
+ , Just $ LiveInstr (mkRegRegMoveInstr platform reg2 reg) Nothing)
- -- gotta keep this instr
+ -- Gotta keep this instr.
| otherwise
- = do -- update the association
- let assoc' = addAssoc (SReg reg) (SSlot slot) -- doing the reload makes reg and slot the same value
- $ delAssoc (SReg reg) -- reg value changes on reload
- $ assoc
+ = do -- Update the association.
+ let assoc'
+ = addAssoc (SReg reg) (SSlot slot)
+ -- doing the reload makes reg and slot the same value
+ $ delAssoc (SReg reg)
+ -- reg value changes on reload
+ $ assoc
- -- remember that this block reloads from this slot
+ -- Remember that this block reloads from this slot.
accBlockReloadsSlot blockId slot
return (assoc', Just li)
@@ -255,10 +260,12 @@ cleanReload _ _ _ _
= panic "RegSpillClean.cleanReload: unhandled instr"
--- | Clean out unneeded spill instructions.
+-------------------------------------------------------------------------------
+-- | Clean out unneeded spill instructions,
+-- while walking backwards over the code.
--
--- If there were no reloads from a slot between a spill and the last one
--- then the slot was never read and we don't need the spill.
+-- If there were no reloads from a slot between a spill and the last one
+-- then the slot was never read and we don't need the spill.
--
-- SPILL r0 -> s1
-- RELOAD s1 -> r2
@@ -274,10 +281,10 @@ cleanReload _ _ _ _
--
-- a) On a spill from a slot
-- If the slot is in set then we can erase the spill,
--- because it won't be reloaded from until after the next spill.
+-- because it won't be reloaded from until after the next spill.
--
-- otherwise
--- keep the spill and add the slot to the set
+-- keep the spill and add the slot to the set
--
-- TODO: This is mostly inter-block
-- we should really be updating the noReloads set as we cross jumps also.
@@ -314,30 +321,32 @@ cleanBlockBackward liveSlotsOnEntry (BasicBlock blockId instrs)
cleanBackward
:: Instruction instr
- => Map BlockId (Set Int) -- ^ Slots live on entry to each block
- -> UniqSet Int -- ^ slots that have been spilled, but not reloaded from
- -> [LiveInstr instr] -- ^ acc
- -> [LiveInstr instr] -- ^ instrs to clean (in forwards order)
- -> CleanM [LiveInstr instr] -- ^ cleaned instrs (in backwards order)
-
+ => Map BlockId (Set Int) -- ^ Slots live on entry to each block
+ -> UniqSet Int -- ^ Slots that have been spilled, but not reloaded from
+ -> [LiveInstr instr] -- ^ acc
+ -> [LiveInstr instr] -- ^ Instrs to clean (in forwards order)
+ -> CleanM [LiveInstr instr] -- ^ Cleaned instrs (in backwards order)
cleanBackward liveSlotsOnEntry noReloads acc lis
= do reloadedBy <- gets sReloadedBy
cleanBackward' liveSlotsOnEntry reloadedBy noReloads acc lis
-cleanBackward' :: Instruction instr
- => Map BlockId (Set Int)
- -> UniqFM [BlockId]
- -> UniqSet Int
- -> [LiveInstr instr]
- -> [LiveInstr instr]
- -> State CleanS [LiveInstr instr]
+
+cleanBackward'
+ :: Instruction instr
+ => Map BlockId (Set Int)
+ -> UniqFM [BlockId]
+ -> UniqSet Int
+ -> [LiveInstr instr]
+ -> [LiveInstr instr]
+ -> State CleanS [LiveInstr instr]
+
cleanBackward' _ _ _ acc []
= return acc
cleanBackward' liveSlotsOnEntry reloadedBy noReloads acc (li : instrs)
- -- if nothing ever reloads from this slot then we don't need the spill
+ -- If nothing ever reloads from this slot then we don't need the spill.
| LiveInstr (SPILL _ slot) _ <- li
, Nothing <- lookupUFM reloadedBy (SSlot slot)
= do modify $ \s -> s { sCleanedSpillsAcc = sCleanedSpillsAcc s + 1 }
@@ -346,13 +355,14 @@ cleanBackward' liveSlotsOnEntry reloadedBy noReloads acc (li : instrs)
| LiveInstr (SPILL _ slot) _ <- li
= if elementOfUniqSet slot noReloads
- -- we can erase this spill because the slot won't be read until after the next one
+ -- We can erase this spill because the slot won't be read until
+ -- after the next one
then do
modify $ \s -> s { sCleanedSpillsAcc = sCleanedSpillsAcc s + 1 }
cleanBackward liveSlotsOnEntry noReloads acc instrs
else do
- -- this slot is being spilled to, but we haven't seen any reloads yet.
+ -- This slot is being spilled to, but we haven't seen any reloads yet.
let noReloads' = addOneToUniqSet noReloads slot
cleanBackward liveSlotsOnEntry noReloads' (li : acc) instrs
@@ -362,20 +372,23 @@ cleanBackward' liveSlotsOnEntry reloadedBy noReloads acc (li : instrs)
= cleanBackward liveSlotsOnEntry noReloads' (li : acc) instrs
-- If a slot is live in a jump target then assume it's reloaded there.
+ --
-- TODO: A real dataflow analysis would do a better job here.
- -- If the target block _ever_ used the slot then we assume it always does,
- -- but if those reloads are cleaned the slot liveness map doesn't get updated.
+ -- If the target block _ever_ used the slot then we assume
+ -- it always does, but if those reloads are cleaned the slot
+ -- liveness map doesn't get updated.
| LiveInstr instr _ <- li
, targets <- jumpDestsOfInstr instr
= do
let slotsReloadedByTargets
- = Set.unions
- $ catMaybes
- $ map (flip Map.lookup liveSlotsOnEntry)
- $ targets
+ = Set.unions
+ $ catMaybes
+ $ map (flip Map.lookup liveSlotsOnEntry)
+ $ targets
- let noReloads' = foldl' delOneFromUniqSet noReloads
- $ Set.toList slotsReloadedByTargets
+ let noReloads'
+ = foldl' delOneFromUniqSet noReloads
+ $ Set.toList slotsReloadedByTargets
cleanBackward liveSlotsOnEntry noReloads' (li : acc) instrs
@@ -384,9 +397,7 @@ cleanBackward' liveSlotsOnEntry reloadedBy noReloads acc (li : instrs)
= cleanBackward liveSlotsOnEntry noReloads (li : acc) instrs
--- collateJoinPoints:
---
--- | combine the associations from all the inward control flow edges.
+-- | Combine the associations from all the inward control flow edges.
--
collateJoinPoints :: CleanM ()
collateJoinPoints
@@ -410,30 +421,36 @@ findRegOfSlot assoc slot
= Nothing
----------------
-type CleanM = State CleanS
+-------------------------------------------------------------------------------
+-- | Cleaner monad.
+type CleanM
+ = State CleanS
+
+-- | Cleaner state.
data CleanS
= CleanS
- { -- regs which are valid at the start of each block.
+ { -- | Regs which are valid at the start of each block.
sJumpValid :: UniqFM (Assoc Store)
- -- collecting up what regs were valid across each jump.
+ -- | Collecting up what regs were valid across each jump.
-- in the next pass we can collate these and write the results
-- to sJumpValid.
, sJumpValidAcc :: UniqFM [Assoc Store]
- -- map of (slot -> blocks which reload from this slot)
+ -- | Map of (slot -> blocks which reload from this slot)
-- used to decide if whether slot spilled to will ever be
-- reloaded from on this path.
, sReloadedBy :: UniqFM [BlockId]
- -- spills\/reloads cleaned each pass (latest at front)
+ -- | Spills and reloads cleaned each pass (latest at front)
, sCleanedCount :: [(Int, Int)]
- -- spills\/reloads that have been cleaned in this pass so far.
+ -- | Spills and reloads that have been cleaned in this pass so far.
, sCleanedSpillsAcc :: Int
, sCleanedReloadsAcc :: Int }
+
+-- | Construct the initial cleaner state.
initCleanS :: CleanS
initCleanS
= CleanS
@@ -448,7 +465,7 @@ initCleanS
, sCleanedReloadsAcc = 0 }
--- | Remember the associations before a jump
+-- | Remember the associations before a jump.
accJumpValid :: Assoc Store -> BlockId -> CleanM ()
accJumpValid assocs target
= modify $ \s -> s {
@@ -467,22 +484,22 @@ accBlockReloadsSlot blockId slot
[blockId] }
---------------
+-------------------------------------------------------------------------------
-- A store location can be a stack slot or a register
---
data Store
= SSlot Int
| SReg Reg
--- | Check if this is a reg store
+
+-- | Check if this is a reg store.
isStoreReg :: Store -> Bool
isStoreReg ss
= case ss of
SSlot _ -> False
SReg _ -> True
--- spill cleaning is only done once all virtuals have been allocated to realRegs
---
+
+-- Spill cleaning is only done once all virtuals have been allocated to realRegs
instance Uniquable Store where
getUnique (SReg r)
| RegReal (RealRegSingle i) <- r
@@ -492,28 +509,30 @@ instance Uniquable Store where
= mkRegPairUnique (r1 * 65535 + r2)
| otherwise
- = error "RegSpillClean.getUnique: found virtual reg during spill clean, only real regs expected."
+ = error $ "RegSpillClean.getUnique: found virtual reg during spill clean,"
+ ++ "only real regs expected."
getUnique (SSlot i) = mkRegSubUnique i -- [SLPJ] I hope "SubUnique" is ok
+
instance Outputable Store where
ppr (SSlot i) = text "slot" <> int i
ppr (SReg r) = ppr r
---------------
+-------------------------------------------------------------------------------
-- Association graphs.
--- In the spill cleaner, two store locations are associated if they are known
--- to hold the same value.
+-- In the spill cleaner, two store locations are associated if they are known
+-- to hold the same value.
--
type Assoc a = UniqFM (UniqSet a)
--- | an empty association
+-- | An empty association
emptyAssoc :: Assoc a
emptyAssoc = emptyUFM
--- | add an association between these two things
+-- | Add an association between these two things.
addAssoc :: Uniquable a
=> a -> a -> Assoc a -> Assoc a
@@ -523,7 +542,7 @@ addAssoc a b m
in m2
--- | delete all associations to a node
+-- | Delete all associations to a node.
delAssoc :: (Outputable a, Uniquable a)
=> a -> Assoc a -> Assoc a
@@ -535,9 +554,9 @@ delAssoc a m
| otherwise = m
--- | delete a single association edge (a -> b)
+-- | Delete a single association edge (a -> b).
delAssoc1 :: Uniquable a
- => a -> a -> Assoc a -> Assoc a
+ => a -> a -> Assoc a -> Assoc a
delAssoc1 a b m
| Just aSet <- lookupUFM m a
@@ -546,14 +565,15 @@ delAssoc1 a b m
| otherwise = m
--- | check if these two things are associated
+-- | Check if these two things are associated.
elemAssoc :: (Outputable a, Uniquable a)
=> a -> a -> Assoc a -> Bool
elemAssoc a b m
= elementOfUniqSet b (closeAssoc a m)
--- | find the refl. trans. closure of the association from this point
+
+-- | Find the refl. trans. closure of the association from this point.
closeAssoc :: (Outputable a, Uniquable a)
=> a -> Assoc a -> UniqSet a
@@ -567,7 +587,6 @@ closeAssoc a assoc
[] -> visited
(x:_)
-
-- we've already seen this node
| elementOfUniqSet x visited
-> closeAssoc' assoc visited (delOneFromUniqSet toVisit x)
@@ -584,7 +603,7 @@ closeAssoc a assoc
(addOneToUniqSet visited x)
(unionUniqSets toVisit neighbors)
--- | intersect
+-- | Intersect two associations.
intersectAssoc
:: Uniquable a
=> Assoc a -> Assoc a -> Assoc a