Better calculation of spill costs / selection of spill candidates.

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.
parent 2381528a
......@@ -44,6 +44,7 @@ module MachRegs (
-- * Machine-dependent register-related stuff
allocatableRegs, argRegs, allArgRegs, callClobberedRegs,
allocatableRegsInClass,
freeReg,
spRel,
......
......@@ -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...
--
......
......@@ -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
......
......@@ -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
......
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) ]
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment