module RegAlloc.Linear.Main (
regAlloc,
module RegAlloc.Linear.Base,
module RegAlloc.Linear.Stats
) where
#include "HsVersions.h"
import RegAlloc.Linear.State
import RegAlloc.Linear.Base
import RegAlloc.Linear.StackMap
import RegAlloc.Linear.FreeRegs
import RegAlloc.Linear.Stats
import RegAlloc.Linear.JoinToTargets
import qualified RegAlloc.Linear.PPC.FreeRegs as PPC
import qualified RegAlloc.Linear.SPARC.FreeRegs as SPARC
import qualified RegAlloc.Linear.X86.FreeRegs as X86
import TargetReg
import RegAlloc.Liveness
import Instruction
import Reg
import BlockId
import OldCmm hiding (RegSet)
import Digraph
import DynFlags
import Unique
import UniqSet
import UniqFM
import UniqSupply
import Outputable
import Platform
import Data.Maybe
import Data.List
import Control.Monad
#include "../includes/stg/MachRegs.h"
regAlloc
:: (Outputable instr, Instruction instr)
=> DynFlags
-> LiveCmmDecl statics instr
-> UniqSM (NatCmmDecl statics instr, Maybe RegAllocStats)
regAlloc _ (CmmData sec d)
= return
( CmmData sec d
, Nothing )
regAlloc _ (CmmProc (LiveInfo info _ _ _) lbl [])
= return ( CmmProc info lbl (ListGraph [])
, Nothing )
regAlloc dflags (CmmProc static lbl sccs)
| LiveInfo info (Just first_id) (Just block_live) _ <- static
= do
(final_blocks, stats)
<- linearRegAlloc dflags first_id block_live sccs
let ((first':_), rest')
= partition ((== first_id) . blockId) final_blocks
return ( CmmProc info lbl (ListGraph (first' : rest'))
, Just stats)
regAlloc _ (CmmProc _ _ _)
= panic "RegAllocLinear.regAlloc: no match"
linearRegAlloc
:: (Outputable instr, Instruction instr)
=> DynFlags
-> BlockId
-> BlockMap RegSet
-> [SCC (LiveBasicBlock instr)]
-> UniqSM ([NatBasicBlock instr], RegAllocStats)
linearRegAlloc dflags first_id block_live sccs
= let platform = targetPlatform dflags
in case platformArch platform of
ArchX86 -> linearRegAlloc' platform (frInitFreeRegs :: X86.FreeRegs) first_id block_live sccs
ArchX86_64 -> linearRegAlloc' platform (frInitFreeRegs :: X86.FreeRegs) first_id block_live sccs
ArchSPARC -> linearRegAlloc' platform (frInitFreeRegs :: SPARC.FreeRegs) first_id block_live sccs
ArchPPC -> linearRegAlloc' platform (frInitFreeRegs :: PPC.FreeRegs) first_id block_live sccs
ArchARM _ _ _ -> panic "linearRegAlloc ArchARM"
ArchPPC_64 -> panic "linearRegAlloc ArchPPC_64"
ArchUnknown -> panic "linearRegAlloc ArchUnknown"
linearRegAlloc'
:: (FR freeRegs, Outputable instr, Instruction instr)
=> Platform
-> freeRegs
-> BlockId
-> BlockMap RegSet
-> [SCC (LiveBasicBlock instr)]
-> UniqSM ([NatBasicBlock instr], RegAllocStats)
linearRegAlloc' platform initFreeRegs first_id block_live sccs
= do us <- getUs
let (_, _, stats, blocks) =
runR emptyBlockMap initFreeRegs emptyRegMap (emptyStackMap platform) us
$ linearRA_SCCs platform first_id block_live [] sccs
return (blocks, stats)
linearRA_SCCs :: (FR freeRegs, Instruction instr, Outputable instr)
=> Platform
-> BlockId
-> BlockMap RegSet
-> [NatBasicBlock instr]
-> [SCC (LiveBasicBlock instr)]
-> RegM freeRegs [NatBasicBlock instr]
linearRA_SCCs _ _ _ blocksAcc []
= return $ reverse blocksAcc
linearRA_SCCs platform first_id block_live blocksAcc (AcyclicSCC block : sccs)
= do blocks' <- processBlock platform block_live block
linearRA_SCCs platform first_id block_live
((reverse blocks') ++ blocksAcc)
sccs
linearRA_SCCs platform first_id block_live blocksAcc (CyclicSCC blocks : sccs)
= do
blockss' <- process platform first_id block_live blocks [] (return []) False
linearRA_SCCs platform first_id block_live
(reverse (concat blockss') ++ blocksAcc)
sccs
process :: (FR freeRegs, Instruction instr, Outputable instr)
=> Platform
-> BlockId
-> BlockMap RegSet
-> [GenBasicBlock (LiveInstr instr)]
-> [GenBasicBlock (LiveInstr instr)]
-> [[NatBasicBlock instr]]
-> Bool
-> RegM freeRegs [[NatBasicBlock instr]]
process _ _ _ [] [] accum _
= return $ reverse accum
process platform first_id block_live [] next_round accum madeProgress
| not madeProgress
= return $ reverse accum
| otherwise
= process platform first_id block_live
next_round [] accum False
process platform first_id block_live (b@(BasicBlock id _) : blocks)
next_round accum madeProgress
= do
block_assig <- getBlockAssigR
if isJust (mapLookup id block_assig)
|| id == first_id
then do
b' <- processBlock platform block_live b
process platform first_id block_live blocks
next_round (b' : accum) True
else process platform first_id block_live blocks
(b : next_round) accum madeProgress
processBlock
:: (FR freeRegs, Outputable instr, Instruction instr)
=> Platform
-> BlockMap RegSet
-> LiveBasicBlock instr
-> RegM freeRegs [NatBasicBlock instr]
processBlock platform block_live (BasicBlock id instrs)
= do initBlock id block_live
(instrs', fixups)
<- linearRA platform block_live [] [] id instrs
return $ BasicBlock id instrs' : fixups
initBlock :: FR freeRegs => BlockId -> BlockMap RegSet -> RegM freeRegs ()
initBlock id block_live
= do block_assig <- getBlockAssigR
case mapLookup id block_assig of
Nothing
-> do
case mapLookup id block_live of
Nothing ->
setFreeRegsR frInitFreeRegs
Just live ->
setFreeRegsR $ foldr frAllocateReg frInitFreeRegs [ r | RegReal r <- uniqSetToList live ]
setAssigR emptyRegMap
Just (freeregs, assig)
-> do setFreeRegsR freeregs
setAssigR assig
linearRA
:: (FR freeRegs, Outputable instr, Instruction instr)
=> Platform
-> BlockMap RegSet
-> [instr]
-> [NatBasicBlock instr]
-> BlockId
-> [LiveInstr instr]
-> RegM freeRegs
( [instr]
, [NatBasicBlock instr])
linearRA _ _ accInstr accFixup _ []
= return
( reverse accInstr
, accFixup)
linearRA platform block_live accInstr accFixups id (instr:instrs)
= do
(accInstr', new_fixups)
<- raInsn platform block_live accInstr id instr
linearRA platform block_live accInstr' (new_fixups ++ accFixups) id instrs
raInsn
:: (FR freeRegs, Outputable instr, Instruction instr)
=> Platform
-> BlockMap RegSet
-> [instr]
-> BlockId
-> LiveInstr instr
-> RegM freeRegs
( [instr]
, [NatBasicBlock instr])
raInsn _ _ new_instrs _ (LiveInstr ii Nothing)
| Just n <- takeDeltaInstr ii
= do setDeltaR n
return (new_instrs, [])
raInsn _ _ new_instrs _ (LiveInstr ii Nothing)
| isMetaInstr ii
= return (new_instrs, [])
raInsn platform block_live new_instrs id (LiveInstr (Instr instr) (Just live))
= do
assig <- getAssigR
case takeRegRegMoveInstr instr of
Just (src,dst) | src `elementOfUniqSet` (liveDieRead live),
isVirtualReg dst,
not (dst `elemUFM` assig),
isRealReg src || isInReg src assig -> do
case src of
(RegReal rr) -> setAssigR (addToUFM assig dst (InReg rr))
_virt -> case lookupUFM assig src of
Nothing -> panic "raInsn"
Just loc ->
setAssigR (addToUFM (delFromUFM assig src) dst loc)
return (new_instrs, [])
_ -> genRaInsn platform block_live new_instrs id instr
(uniqSetToList $ liveDieRead live)
(uniqSetToList $ liveDieWrite live)
raInsn _ _ _ _ instr
= pprPanic "raInsn" (text "no match for:" <> ppr instr)
isInReg :: Reg -> RegMap Loc -> Bool
isInReg src assig | Just (InReg _) <- lookupUFM assig src = True
| otherwise = False
genRaInsn :: (FR freeRegs, Instruction instr, Outputable instr)
=> Platform
-> BlockMap RegSet
-> [instr]
-> BlockId
-> instr
-> [Reg]
-> [Reg]
-> RegM freeRegs ([instr], [NatBasicBlock instr])
genRaInsn platform block_live new_instrs block_id instr r_dying w_dying =
case regUsageOfInstr instr of { RU read written ->
do
let real_written = [ rr | (RegReal rr) <- written ]
let virt_written = [ vr | (RegVirtual vr) <- written ]
let virt_read = nub [ vr | (RegVirtual vr) <- read ]
clobber_saves <- saveClobberedTemps platform real_written r_dying
(r_spills, r_allocd) <-
allocateRegsAndSpill platform True virt_read [] [] virt_read
(fixup_blocks, adjusted_instr)
<- joinToTargets platform block_live block_id instr
releaseRegs r_dying
clobberRegs real_written
(w_spills, w_allocd) <-
allocateRegsAndSpill platform False virt_written [] [] virt_written
releaseRegs w_dying
let
patch_map
= listToUFM
[ (t, RegReal r)
| (t, r) <- zip virt_read r_allocd
++ zip virt_written w_allocd ]
patched_instr
= patchRegsOfInstr adjusted_instr patchLookup
patchLookup x
= case lookupUFM patch_map x of
Nothing -> x
Just y -> y
let squashed_instr = case takeRegRegMoveInstr patched_instr of
Just (src, dst)
| src == dst -> []
_ -> [patched_instr]
let code = squashed_instr ++ w_spills ++ reverse r_spills
++ clobber_saves ++ new_instrs
return (code, fixup_blocks)
}
releaseRegs :: FR freeRegs => [Reg] -> RegM freeRegs ()
releaseRegs regs = do
assig <- getAssigR
free <- getFreeRegsR
loop assig free regs
where
loop _ free _ | free `seq` False = undefined
loop assig free [] = do setAssigR assig; setFreeRegsR free; return ()
loop assig free (RegReal rr : rs) = loop assig (frReleaseReg rr free) rs
loop assig free (r:rs) =
case lookupUFM assig r of
Just (InBoth real _) -> loop (delFromUFM assig r) (frReleaseReg real free) rs
Just (InReg real) -> loop (delFromUFM assig r) (frReleaseReg real free) rs
_other -> loop (delFromUFM assig r) free rs
saveClobberedTemps
:: (Outputable instr, Instruction instr)
=> Platform
-> [RealReg]
-> [Reg]
-> RegM freeRegs [instr]
saveClobberedTemps _ [] _
= return []
saveClobberedTemps platform clobbered dying
= do
assig <- getAssigR
let to_spill
= [ (temp,reg)
| (temp, InReg reg) <- ufmToList assig
, any (realRegsAlias reg) clobbered
, temp `notElem` map getUnique dying ]
(instrs,assig') <- clobber assig [] to_spill
setAssigR assig'
return instrs
where
clobber assig instrs []
= return (instrs, assig)
clobber assig instrs ((temp, reg) : rest)
= do
(spill, slot) <- spillR platform (RegReal reg) temp
recordSpill (SpillClobber temp)
let new_assign = addToUFM assig temp (InBoth reg slot)
clobber new_assign (spill : instrs) rest
clobberRegs :: FR freeRegs => [RealReg] -> RegM freeRegs ()
clobberRegs []
= return ()
clobberRegs clobbered
= do
freeregs <- getFreeRegsR
setFreeRegsR $! foldr frAllocateReg freeregs clobbered
assig <- getAssigR
setAssigR $! clobber assig (ufmToList assig)
where
clobber assig []
= assig
clobber assig ((temp, InBoth reg slot) : rest)
| any (realRegsAlias reg) clobbered
= clobber (addToUFM assig temp (InMem slot)) rest
clobber assig (_:rest)
= clobber assig rest
data SpillLoc = ReadMem StackSlot
| WriteNew
| WriteMem
allocateRegsAndSpill
:: (FR freeRegs, Outputable instr, Instruction instr)
=> Platform
-> Bool
-> [VirtualReg]
-> [instr]
-> [RealReg]
-> [VirtualReg]
-> RegM freeRegs ( [instr] , [RealReg])
allocateRegsAndSpill _ _ _ spills alloc []
= return (spills, reverse alloc)
allocateRegsAndSpill platform reading keep spills alloc (r:rs)
= do assig <- getAssigR
let doSpill = allocRegsAndSpill_spill platform reading keep spills alloc r rs assig
case lookupUFM assig r of
Just (InReg my_reg) ->
allocateRegsAndSpill platform reading keep spills (my_reg:alloc) rs
Just (InBoth my_reg _)
-> do when (not reading) (setAssigR (addToUFM assig r (InReg my_reg)))
allocateRegsAndSpill platform reading keep spills (my_reg:alloc) rs
Just (InMem slot) | reading -> doSpill (ReadMem slot)
| otherwise -> doSpill WriteMem
Nothing | reading ->
doSpill WriteNew
| otherwise -> doSpill WriteNew
allocRegsAndSpill_spill :: (FR freeRegs, Instruction instr, Outputable instr)
=> Platform
-> Bool
-> [VirtualReg]
-> [instr]
-> [RealReg]
-> VirtualReg
-> [VirtualReg]
-> UniqFM Loc
-> SpillLoc
-> RegM freeRegs ([instr], [RealReg])
allocRegsAndSpill_spill platform reading keep spills alloc r rs assig spill_loc
= do
freeRegs <- getFreeRegsR
let freeRegs_thisClass = frGetFreeRegs (classOfVirtualReg r) freeRegs
case freeRegs_thisClass of
(my_reg : _) ->
do spills' <- loadTemp platform r spill_loc my_reg spills
setAssigR (addToUFM assig r $! newLocation spill_loc my_reg)
setFreeRegsR $ frAllocateReg my_reg freeRegs
allocateRegsAndSpill platform reading keep spills' (my_reg : alloc) rs
[] ->
do let keep' = map getUnique keep
let candidates_inBoth
= [ (temp, reg, mem)
| (temp, InBoth reg mem) <- ufmToList assig
, temp `notElem` keep'
, targetClassOfRealReg platform reg == classOfVirtualReg r ]
let candidates_inReg
= [ (temp, reg)
| (temp, InReg reg) <- ufmToList assig
, temp `notElem` keep'
, targetClassOfRealReg platform reg == classOfVirtualReg r ]
let result
| (temp, my_reg, slot) : _ <- candidates_inBoth
= do spills' <- loadTemp platform r spill_loc my_reg spills
let assig1 = addToUFM assig temp (InMem slot)
let assig2 = addToUFM assig1 r $! newLocation spill_loc my_reg
setAssigR assig2
allocateRegsAndSpill platform reading keep spills' (my_reg:alloc) rs
| (temp_to_push_out, (my_reg :: RealReg)) : _
<- candidates_inReg
= do
(spill_insn, slot) <- spillR platform (RegReal my_reg) temp_to_push_out
let spill_store = (if reading then id else reverse)
[
spill_insn ]
recordSpill (SpillAlloc temp_to_push_out)
let assig1 = addToUFM assig temp_to_push_out (InMem slot)
let assig2 = addToUFM assig1 r $! newLocation spill_loc my_reg
setAssigR assig2
spills' <- loadTemp platform r spill_loc my_reg spills
allocateRegsAndSpill platform reading keep
(spill_store ++ spills')
(my_reg:alloc) rs
| otherwise
= pprPanic ("RegAllocLinear.allocRegsAndSpill: no spill candidates\n")
$ vcat
[ text "allocating vreg: " <> text (show r)
, text "assignment: " <> text (show $ ufmToList assig)
, text "freeRegs: " <> text (show freeRegs)
, text "initFreeRegs: " <> text (show (frInitFreeRegs `asTypeOf` freeRegs)) ]
result
newLocation :: SpillLoc -> RealReg -> Loc
newLocation (ReadMem slot) my_reg = InBoth my_reg slot
newLocation _ my_reg = InReg my_reg
loadTemp
:: (Outputable instr, Instruction instr)
=> Platform
-> VirtualReg
-> SpillLoc
-> RealReg
-> [instr]
-> RegM freeRegs [instr]
loadTemp platform vreg (ReadMem slot) hreg spills
= do
insn <- loadR platform (RegReal hreg) slot
recordSpill (SpillLoad $ getUnique vreg)
return $ insn : spills
loadTemp _ _ _ _ spills =
return spills