Better calculation of spill costs / selection of spill candidates.
authorBen.Lippmeier@anu.edu.au <unknown>
Thu, 13 Sep 2007 15:54:07 +0000 (15:54 +0000)
committerBen.Lippmeier@anu.edu.au <unknown>
Thu, 13 Sep 2007 15:54:07 +0000 (15:54 +0000)
Use Chaitin's formula for calculation of spill costs.
  Cost to spill some vreg = (num writes + num reads) / degree of node

With 2 extra provisos:
  1) Don't spill vregs that live for only 1 instruction.
  2) Always prefer to spill vregs that live for a number of instructions
       more than 10 times the number of vregs in that class.

Proviso 2 is there to help deal with basic blocks containing very long
live ranges - SHA1 has live ranges > 1700 instructions. We don't ever
try to keep these long lived ranges in regs at the expense of others.

Because stack slots are allocated from a global pool, and there is no
slot coalescing yet, without this condition the allocation of SHA1 dosn't
converge fast enough and eventually runs out of stack slots.

Prior to this patch we were just choosing to spill the range with the
longest lifetime, so we didn't bump into this particular problem.

compiler/nativeGen/MachRegs.lhs
compiler/nativeGen/RegAllocColor.hs
compiler/nativeGen/RegAllocStats.hs
compiler/nativeGen/RegLiveness.hs
compiler/nativeGen/RegSpillCost.hs [new file with mode: 0644]

index ee514f9..beea181 100644 (file)
@@ -44,6 +44,7 @@ module MachRegs (
 
        -- * Machine-dependent register-related stuff
         allocatableRegs, argRegs, allArgRegs, callClobberedRegs,
+       allocatableRegsInClass,
        freeReg,
        spRel,
 
index 35550dd..48e64bf 100644 (file)
@@ -17,6 +17,7 @@ import qualified GraphColor   as Color
 import RegLiveness
 import RegSpill
 import RegSpillClean
+import RegSpillCost
 import RegAllocStats
 -- import RegCoalesce
 import MachRegs
@@ -92,7 +93,6 @@ regAlloc_spin dflags (spinCount :: Int) triv regsFree slotsFree debug_codeGraphs
 
        let code_coalesced1     = code
 
-
        -- build a conflict graph from the code.
        graph           <- {-# SCC "BuildGraph" #-} buildGraph code_coalesced1
 
@@ -104,11 +104,13 @@ regAlloc_spin dflags (spinCount :: Int) triv regsFree slotsFree debug_codeGraphs
        seqGraph graph `seq` return ()
 
 
-       -- build a map of how many instructions each reg lives for.
-       --      this is lazy, it won't be computed unless we need to spill
+       -- build a map of the cost of spilling each instruction
+       --      this will only actually be computed if we have to spill something.
+       let spillCosts  = foldl' plusSpillCostInfo zeroSpillCostInfo
+                       $ map slurpSpillCostInfo code_coalesced1
 
-       let fmLife      = {-# SCC "LifetimeCount" #-} plusUFMs_C (\(r1, l1) (_, l2) -> (r1, l1 + l2))
-                       $ map lifetimeCount code_coalesced1
+       -- the function to choose regs to leave uncolored
+       let spill       = chooseSpill spillCosts
 
        -- record startup state
        let stat1       =
@@ -116,12 +118,8 @@ regAlloc_spin dflags (spinCount :: Int) triv regsFree slotsFree debug_codeGraphs
                 then   Just $ RegAllocStatsStart
                        { raLiveCmm     = code
                        , raGraph       = graph
-                       , raLifetimes   = fmLife }
+                       , raSpillCosts  = spillCosts }
                 else   Nothing
-
-
-       -- the function to choose regs to leave uncolored
-       let spill       = chooseSpill_maxLife fmLife
        
        -- try and color the graph 
        let (graph_colored, rsSpill, rmCoalesce)
@@ -177,6 +175,7 @@ regAlloc_spin dflags (spinCount :: Int) triv regsFree slotsFree debug_codeGraphs
                        , statList
                        , graph_colored)
 
+        -- we couldn't find a coloring, time to spill something
         else do
                -- spill the uncolored regs
                (code_spilled, slotsFree', spillStats)
@@ -192,7 +191,7 @@ regAlloc_spin dflags (spinCount :: Int) triv regsFree slotsFree debug_codeGraphs
                        { raGraph       = graph_colored
                        , raCoalesced   = rmCoalesce
                        , raSpillStats  = spillStats
-                       , raLifetimes   = fmLife
+                       , raSpillCosts  = spillCosts
                        , raSpilled     = code_spilled }
                                
                let statList =
@@ -207,46 +206,7 @@ regAlloc_spin dflags (spinCount :: Int) triv regsFree slotsFree debug_codeGraphs
                        statList
                        code_relive
 
------
--- Simple maxconflicts isn't always good, because we
---     can naievely end up spilling vregs that only live for one or two instrs.
---     
-{-
-chooseSpill_maxConflicts
-       :: Color.Graph Reg RegClass Reg
-       -> Reg
-       
-chooseSpill_maxConflicts graph
- = let node    = maximumBy 
-                       (\n1 n2 -> compare 
-                               (sizeUniqSet $ Color.nodeConflicts n1) 
-                               (sizeUniqSet $ Color.nodeConflicts n2))
-               $ eltsUFM $ Color.graphMap graph
-               
-   in  Color.nodeId node
--} 
-   
------
-chooseSpill_maxLife
-       :: UniqFM (Reg, Int)
-       -> Color.Graph Reg RegClass Reg
-       -> Reg
-
-chooseSpill_maxLife life graph
- = let node    = maximumBy (\n1 n2 -> compare (getLife n1) (getLife n2))
-               $ eltsUFM $ Color.graphMap graph
 
-       -- Orphan vregs die in the same instruction they are born in.
-       --      They will be in the graph, but not in the liveness map.
-       --      Their liveness is 0.
-       getLife n
-        = case lookupUFM life (Color.nodeId n) of
-               Just (_, l)     -> l
-               Nothing         -> 0
-
-   in  Color.nodeId node
-   
 
 -- | Build a graph from the liveness and coalesce information in this code.
 
@@ -346,11 +306,6 @@ patchRegsFromGraph graph code
    in  patchEraseLive patchF code
    
 
-plusUFMs_C  :: (elt -> elt -> elt) -> [UniqFM elt] -> UniqFM elt
-plusUFMs_C f maps
-       = foldl' (plusUFM_C f) emptyUFM maps
-
-
 -----
 -- for when laziness just isn't what you wanted...
 --
index 728225a..81578a3 100644 (file)
@@ -24,6 +24,7 @@ import qualified GraphColor as Color
 import RegLiveness
 import RegAllocInfo
 import RegSpill
+import RegSpillCost
 import MachRegs
 import MachInstrs
 import Cmm
@@ -40,15 +41,15 @@ data RegAllocStats
        -- initial graph
        = RegAllocStatsStart
        { raLiveCmm     :: [LiveCmmTop]                   -- ^ initial code, with liveness
-       , raGraph       :: Color.Graph Reg RegClass Reg  -- ^ the initial, uncolored graph
-       , raLifetimes   :: UniqFM (Reg, Int) }            -- ^ number of instrs each reg lives for
+       , raGraph       :: Color.Graph Reg RegClass Reg   -- ^ the initial, uncolored graph
+       , raSpillCosts  :: SpillCostInfo }                -- ^ information to help choose which regs to spill
 
        -- a spill stage
        | RegAllocStatsSpill
        { raGraph       :: Color.Graph Reg RegClass Reg -- ^ the partially colored graph
        , raCoalesced   :: UniqFM Reg                   -- ^ the regs that were coaleced
        , raSpillStats  :: SpillStats                   -- ^ spiller stats
-       , raLifetimes   :: UniqFM (Reg, Int)            -- ^ number of instrs each reg lives for
+       , raSpillCosts  :: SpillCostInfo                -- ^ number of instrs each reg lives for
        , raSpilled     :: [LiveCmmTop] }               -- ^ code with spill instructions added
 
        -- a successful coloring
@@ -84,6 +85,10 @@ instance Outputable RegAllocStats where
                        $$ text ""
                else empty)
 
+       $$ text "#  Spill costs.  reg uses defs lifetime degree cost"
+       $$ vcat (map (pprSpillCostRecord (raGraph s)) $ eltsUFM $ raSpillCosts s)
+       $$ text ""
+
        $$ text "#  Spills inserted."
        $$ ppr (raSpillStats s)
        $$ text ""
@@ -156,9 +161,11 @@ pprStatsLifetimes
        :: [RegAllocStats] -> SDoc
 
 pprStatsLifetimes stats
- = let lifeMap         = foldl' plusUFM emptyUFM
-                               [ raLifetimes s | s@RegAllocStatsStart{} <- stats ]
-       lifeBins        = binLifetimeCount lifeMap
+ = let info            = foldl' plusSpillCostInfo zeroSpillCostInfo
+                               [ raSpillCosts s
+                                       | s@RegAllocStatsStart{} <- stats ]
+
+       lifeBins        = binLifetimeCount $ lifeMapFromSpillCostInfo info
 
    in  (  text "-- vreg-population-lifetimes"
        $$ text "--   (instruction_count, number_of_vregs_that_lived_that_long)"
@@ -201,8 +208,9 @@ pprStatsLifeConflict
        -> SDoc
 
 pprStatsLifeConflict stats graph
- = let lifeMap = foldl' plusUFM emptyUFM
-                       [ raLifetimes s | s@RegAllocStatsStart{} <- stats ]
+ = let lifeMap = lifeMapFromSpillCostInfo
+               $ foldl' plusSpillCostInfo zeroSpillCostInfo
+               $ [ raSpillCosts s | s@RegAllocStatsStart{} <- stats ]
 
        scatter = map   (\r ->  let lifetime    = case lookupUFM lifeMap r of
                                                        Just (_, l)     -> l
index e18931c..9ee9897 100644 (file)
@@ -23,7 +23,6 @@ module RegLiveness (
        spillNatBlock,
        slurpConflicts,
        slurpReloadCoalesce,
-       lifetimeCount,
        eraseDeltasLive,
        patchEraseLive,
        patchRegsLiveInstr,
@@ -381,48 +380,6 @@ spillNatBlock (BasicBlock i is)
         =      spillNat (instr : acc) instrs
 
 
--- | Slurp out a map of how many times each register was live upon entry to an instruction.
-
-lifetimeCount
-       :: LiveCmmTop
-       -> UniqFM (Reg, Int)    -- ^ reg -> (reg, count)
-
-lifetimeCount cmm
-       = countCmm emptyUFM cmm
- where
-       countCmm fm  CmmData{}          = fm
-       countCmm fm (CmmProc info _ _ (ListGraph blocks))
-               = foldl' (countComp info) fm blocks
-               
-       countComp info fm (BasicBlock _ blocks)
-               = foldl' (countBlock info) fm blocks
-               
-       countBlock info fm (BasicBlock blockId instrs)
-               | LiveInfo _ _ blockLive        <- info
-               , Just rsLiveEntry              <- lookupUFM blockLive blockId
-               = countLIs rsLiveEntry fm instrs
-
-               | otherwise
-               = error "RegLiveness.countBlock: bad block"
-               
-       countLIs _      fm []                           = fm
-       countLIs rsLive fm (Instr _ Nothing : lis)      = countLIs rsLive fm lis
-       
-       countLIs rsLiveEntry fm (Instr _ (Just live) : lis)
-        = let
-               rsLiveAcross    = rsLiveEntry `minusUniqSet` (liveDieRead live)
-
-               rsLiveNext      = (rsLiveAcross `unionUniqSets` (liveBorn     live))
-                                                `minusUniqSet` (liveDieWrite live)
-
-               add r fm        = addToUFM_C
-                                       (\(r1, l1) (_, l2) -> (r1, l1 + l2))
-                                       fm r (r, 1)
-
-               fm'             = foldUniqSet add fm rsLiveEntry
-          in   countLIs rsLiveNext fm' lis
-          
-
 -- | Erase Delta instructions.
 
 eraseDeltasLive :: LiveCmmTop -> LiveCmmTop
diff --git a/compiler/nativeGen/RegSpillCost.hs b/compiler/nativeGen/RegSpillCost.hs
new file mode 100644 (file)
index 0000000..e639c67
--- /dev/null
@@ -0,0 +1,233 @@
+
+module RegSpillCost (
+       SpillCostRecord,
+       plusSpillCostRecord,
+       pprSpillCostRecord,
+
+       SpillCostInfo,
+       zeroSpillCostInfo,
+       plusSpillCostInfo,
+
+       slurpSpillCostInfo,
+       chooseSpill,
+
+       lifeMapFromSpillCostInfo
+)
+
+where
+
+import GraphBase
+import RegLiveness
+import RegAllocInfo
+import MachInstrs
+import MachRegs
+import Cmm
+
+import UniqFM
+import UniqSet
+import Outputable
+import State
+
+import Data.List       (nub, minimumBy)
+import Data.Maybe
+import Control.Monad
+
+type SpillCostRecord
+ =     ( Reg   -- register name
+       , Int   -- number of writes to this reg
+       , Int   -- number of reads from this reg
+       , Int)  -- number of instrs this reg was live on entry to
+
+type SpillCostInfo
+       = UniqFM SpillCostRecord
+
+
+zeroSpillCostInfo :: SpillCostInfo
+zeroSpillCostInfo      = emptyUFM
+
+-- | Add two spillCostInfos
+plusSpillCostInfo :: SpillCostInfo -> SpillCostInfo -> SpillCostInfo
+plusSpillCostInfo sc1 sc2
+       = plusUFM_C plusSpillCostRecord sc1 sc2
+
+plusSpillCostRecord :: SpillCostRecord -> SpillCostRecord -> SpillCostRecord
+plusSpillCostRecord (r1, a1, b1, c1) (r2, a2, b2, c2)
+       | r1 == r2      = (r1, a1 + a2, b1 + b2, c1 + c2)
+       | otherwise     = error "RegSpillCost.plusRegInt: regs don't match"
+
+
+-- | Slurp out information used for determining spill costs
+--     for each vreg, the number of times it was written to, read from,
+--     and the number of instructions it was live on entry to (lifetime)
+--
+slurpSpillCostInfo
+       :: LiveCmmTop
+       -> SpillCostInfo
+
+slurpSpillCostInfo cmm
+       = execState (countCmm cmm) zeroSpillCostInfo
+ where
+       countCmm CmmData{}              = return ()
+       countCmm (CmmProc info _ _ (ListGraph blocks))
+               = mapM_ (countComp info) blocks
+
+       countComp info (BasicBlock _ blocks)
+               = mapM_ (countBlock info) blocks
+
+       -- lookup the regs that are live on entry to this block in
+       --      the info table from the CmmProc
+       countBlock info (BasicBlock blockId instrs)
+               | LiveInfo _ _ blockLive        <- info
+               , Just rsLiveEntry              <- lookupUFM blockLive blockId
+               = countLIs rsLiveEntry instrs
+
+               | otherwise
+               = error "RegLiveness.slurpSpillCostInfo: bad block"
+
+       countLIs _      []
+               = return ()
+
+       -- skip over comment and delta pseudo instrs
+       countLIs rsLive (Instr instr Nothing : lis)
+               | COMMENT{}     <- instr
+               = countLIs rsLive lis
+
+               | DELTA{}       <- instr
+               = countLIs rsLive lis
+
+               | otherwise
+               = pprPanic "RegSpillCost.slurpSpillCostInfo"
+                       (text "no liveness information on instruction " <> ppr instr)
+
+       countLIs rsLiveEntry (Instr instr (Just live) : lis)
+        = do
+               -- increment the lifetime counts for regs live on entry to this instr
+               mapM_ incLifetime $ uniqSetToList rsLiveEntry
+
+               -- increment counts for what regs were read/written from
+               let (RU read written)   = regUsage instr
+               mapM_ incUses   $ filter (not . isRealReg) $ nub read
+               mapM_ incDefs   $ filter (not . isRealReg) $ nub written
+
+               -- compute liveness for entry to next instruction.
+               let rsLiveAcross
+                       = rsLiveEntry `minusUniqSet` (liveDieRead live)
+
+               let rsLiveNext
+                       = (rsLiveAcross `unionUniqSets` (liveBorn     live))
+                                       `minusUniqSet` (liveDieWrite live)
+
+               countLIs rsLiveNext lis
+
+       incDefs     reg = modify $ \s -> addToUFM_C plusSpillCostRecord s reg (reg, 1, 0, 0)
+       incUses     reg = modify $ \s -> addToUFM_C plusSpillCostRecord s reg (reg, 0, 1, 0)
+       incLifetime reg = modify $ \s -> addToUFM_C plusSpillCostRecord s reg (reg, 0, 0, 1)
+
+
+-- | Choose a node to spill from this graph
+
+chooseSpill
+       :: SpillCostInfo
+       -> Graph Reg RegClass Reg
+       -> Reg
+
+chooseSpill info graph
+ = let cost    = spillCost info graph
+       node    = minimumBy (\n1 n2 -> compare (cost $ nodeId n1) (cost $ nodeId n2))
+               $ eltsUFM $ graphMap graph
+
+   in  nodeId node
+
+
+
+-- | Chaitins spill cost function is:
+--
+--          cost =     sum         loadCost * freq (u)  +    sum        storeCost * freq (d)
+--                  u <- uses (v)                         d <- defs (v)
+--
+--     There are no loops in our code at the momemnt, so we can set the freq's to 1
+--     We divide this by the degree if t
+--
+--
+--  If we don't have live range splitting then Chaitins function performs badly if we have
+--     lots of nested live ranges and very few registers.
+--
+--              v1 v2 v3
+--     def v1   .
+--     use v1   .
+--     def v2   .  .
+--     def v3   .  .  .
+--     use v1   .  .  .
+--     use v3   .  .  .
+--     use v2   .  .
+--     use v1   .
+--
+--
+--           defs uses degree   cost
+--     v1:  1     3     3      1.5
+--     v2:  1     2     3      1.0
+--     v3:  1     1     3      0.666
+--
+--     v3 has the lowest cost, but if we only have 2 hardregs and we insert spill code for v3
+--     then this isn't going to improve the colorability of the graph.
+--
+--  When compiling SHA1, which as very long basic blocks and some vregs with very long live ranges
+--     the allocator seems to try and spill from the inside out and eventually run out of stack slots.
+--
+--  Without live range splitting, its's better to spill from the outside in so set the cost of very
+--     long live ranges to zero
+--
+
+spillCost
+       :: SpillCostInfo
+       -> Graph Reg RegClass Reg
+       -> Reg
+       -> Float
+
+spillCost info graph reg
+       -- Spilling a live range that only lives for 1 instruction isn't going to help
+       --      us at all - and we definately want to avoid trying to re-spill previously
+       --      inserted spill code.
+       | lifetime <= 1         = 1/0
+
+       -- It's unlikely that we'll find a reg for a live range this long
+       --      better to spill it straight up and not risk trying to keep it around
+       --      and have to go through the build/color cycle again.
+       | lifetime > allocatableRegsInClass (regClass reg) * 10
+       = 0
+
+       -- otherwise revert to chaitin's regular cost function.
+       | otherwise     = fromIntegral (uses + defs) / fromIntegral (nodeDegree graph reg)
+       where (_, defs, uses, lifetime)
+               = fromMaybe (reg, 0, 0, 0) $ lookupUFM info reg
+
+
+lifeMapFromSpillCostInfo :: SpillCostInfo -> UniqFM (Reg, Int)
+lifeMapFromSpillCostInfo info
+       = listToUFM
+       $ map (\(r, _, _, life) -> (r, (r, life)))
+       $ eltsUFM info
+
+
+-- | Work out the degree (number of neighbors) of this node which have the same class.
+nodeDegree :: Graph Reg RegClass Reg -> Reg -> Int
+nodeDegree graph reg
+       | Just node     <- lookupUFM (graphMap graph) reg
+       , virtConflicts <- length       $ filter (\r -> regClass r == regClass reg)
+                                       $ uniqSetToList $ nodeConflicts node
+       = virtConflicts + sizeUniqSet (nodeExclusions node)
+
+       | otherwise
+       = 0
+
+
+-- | Show a spill cost record, including the degree from the graph and final calulated spill cos
+pprSpillCostRecord :: Graph Reg RegClass Reg -> SpillCostRecord -> SDoc
+pprSpillCostRecord graph (reg, uses, defs, life)
+       =  hsep
+       [ ppr reg
+       , ppr uses
+       , ppr defs
+       , ppr life
+       , ppr $ nodeDegree graph reg
+       , text $ show $ (fromIntegral (uses + defs) / fromIntegral (nodeDegree graph reg) :: Float) ]