diff options
author | Ben Lippmeier <benl@ouroborus.net> | 2012-11-12 15:10:51 +1100 |
---|---|---|
committer | Ben Lippmeier <benl@ouroborus.net> | 2012-11-14 17:04:56 +1100 |
commit | cace1caf905e0503176e93769238fbcec5283477 (patch) | |
tree | daa28f77bb228fd66a9e25dc0127c618e861d9d6 /compiler/nativeGen/RegAlloc | |
parent | b13ebb673871f06ef12c6358eecbcd8572a1a5f3 (diff) | |
download | haskell-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.hs | 313 |
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 |