module GHC.CmmToAsm.Reg.Linear (
regAlloc,
module GHC.CmmToAsm.Reg.Linear.Base,
module GHC.CmmToAsm.Reg.Linear.Stats
) where
#include "HsVersions.h"
import GHC.Prelude
import GHC.CmmToAsm.Reg.Linear.State
import GHC.CmmToAsm.Reg.Linear.Base
import GHC.CmmToAsm.Reg.Linear.StackMap
import GHC.CmmToAsm.Reg.Linear.FreeRegs
import GHC.CmmToAsm.Reg.Linear.Stats
import GHC.CmmToAsm.Reg.Linear.JoinToTargets
import qualified GHC.CmmToAsm.Reg.Linear.PPC as PPC
import qualified GHC.CmmToAsm.Reg.Linear.SPARC as SPARC
import qualified GHC.CmmToAsm.Reg.Linear.X86 as X86
import qualified GHC.CmmToAsm.Reg.Linear.X86_64 as X86_64
import GHC.CmmToAsm.Reg.Target
import GHC.CmmToAsm.Reg.Liveness
import GHC.CmmToAsm.Reg.Utils
import GHC.CmmToAsm.Instr
import GHC.CmmToAsm.Config
import GHC.CmmToAsm.Types
import GHC.Platform.Reg
import GHC.Cmm.BlockId
import GHC.Cmm.Dataflow.Collections
import GHC.Cmm hiding (RegSet)
import GHC.Data.Graph.Directed
import GHC.Types.Unique
import GHC.Types.Unique.Set
import GHC.Types.Unique.FM
import GHC.Types.Unique.Supply
import GHC.Utils.Outputable
import GHC.Utils.Panic
import GHC.Platform
import Data.Maybe
import Data.List (partition, nub)
import Control.Monad
import Control.Applicative
regAlloc
:: Instruction instr
=> NCGConfig
-> LiveCmmDecl statics instr
-> UniqSM ( NatCmmDecl statics instr
, Maybe Int
, Maybe RegAllocStats
)
regAlloc _ (CmmData sec d)
= return
( CmmData sec d
, Nothing
, Nothing )
regAlloc _ (CmmProc (LiveInfo info _ _ _) lbl live [])
= return ( CmmProc info lbl live (ListGraph [])
, Nothing
, Nothing )
regAlloc config (CmmProc static lbl live sccs)
| LiveInfo info entry_ids@(first_id:_) block_live _ <- static
= do
!(!final_blocks, !stats, !stack_use)
<- linearRegAlloc config entry_ids block_live sccs
let !(!(!first':_), !rest')
= partition ((== first_id) . blockId) final_blocks
let max_spill_slots = maxSpillSlots config
extra_stack
| stack_use > max_spill_slots
= Just $! stack_use max_spill_slots
| otherwise
= Nothing
return ( CmmProc info lbl live (ListGraph (first' : rest'))
, extra_stack
, Just stats)
regAlloc _ (CmmProc _ _ _ _)
= panic "RegAllocLinear.regAlloc: no match"
linearRegAlloc
:: forall instr. Instruction instr
=> NCGConfig
-> [BlockId]
-> BlockMap RegSet
-> [SCC (LiveBasicBlock instr)]
-> UniqSM ([NatBasicBlock instr], RegAllocStats, Int)
linearRegAlloc config entry_ids block_live sccs
= case platformArch platform of
ArchX86 -> go $ (frInitFreeRegs platform :: X86.FreeRegs)
ArchX86_64 -> go $ (frInitFreeRegs platform :: X86_64.FreeRegs)
ArchS390X -> panic "linearRegAlloc ArchS390X"
ArchSPARC -> go $ (frInitFreeRegs platform :: SPARC.FreeRegs)
ArchSPARC64 -> panic "linearRegAlloc ArchSPARC64"
ArchPPC -> go $ (frInitFreeRegs platform :: PPC.FreeRegs)
ArchARM _ _ _ -> panic "linearRegAlloc ArchARM"
ArchAArch64 -> panic "linearRegAlloc ArchAArch64"
ArchPPC_64 _ -> go $ (frInitFreeRegs platform :: PPC.FreeRegs)
ArchAlpha -> panic "linearRegAlloc ArchAlpha"
ArchMipseb -> panic "linearRegAlloc ArchMipseb"
ArchMipsel -> panic "linearRegAlloc ArchMipsel"
ArchRISCV64 -> panic "linearRegAlloc ArchRISCV64"
ArchJavaScript -> panic "linearRegAlloc ArchJavaScript"
ArchUnknown -> panic "linearRegAlloc ArchUnknown"
where
go :: (FR regs, Outputable regs)
=> regs -> UniqSM ([NatBasicBlock instr], RegAllocStats, Int)
go f = linearRegAlloc' config f entry_ids block_live sccs
platform = ncgPlatform config
type OutputableRegConstraint freeRegs instr =
(FR freeRegs, Outputable freeRegs, Instruction instr)
linearRegAlloc'
:: OutputableRegConstraint freeRegs instr
=> NCGConfig
-> freeRegs
-> [BlockId]
-> BlockMap RegSet
-> [SCC (LiveBasicBlock instr)]
-> UniqSM ([NatBasicBlock instr], RegAllocStats, Int)
linearRegAlloc' config initFreeRegs entry_ids block_live sccs
= do us <- getUniqueSupplyM
let !(_, !stack, !stats, !blocks) =
runR config mapEmpty initFreeRegs emptyRegMap emptyStackMap us
$ linearRA_SCCs entry_ids block_live [] sccs
return (blocks, stats, getStackUse stack)
linearRA_SCCs :: OutputableRegConstraint freeRegs instr
=> [BlockId]
-> BlockMap RegSet
-> [NatBasicBlock instr]
-> [SCC (LiveBasicBlock instr)]
-> RegM freeRegs [NatBasicBlock instr]
linearRA_SCCs _ _ blocksAcc []
= return $ reverse blocksAcc
linearRA_SCCs entry_ids block_live blocksAcc (AcyclicSCC block : sccs)
= do blocks' <- processBlock block_live block
linearRA_SCCs entry_ids block_live
((reverse blocks') ++ blocksAcc)
sccs
linearRA_SCCs entry_ids block_live blocksAcc (CyclicSCC blocks : sccs)
= do
blockss' <- process entry_ids block_live blocks
linearRA_SCCs entry_ids block_live
(reverse (concat blockss') ++ blocksAcc)
sccs
process :: forall freeRegs instr. (OutputableRegConstraint freeRegs instr)
=> [BlockId]
-> BlockMap RegSet
-> [GenBasicBlock (LiveInstr instr)]
-> RegM freeRegs [[NatBasicBlock instr]]
process entry_ids block_live =
\blocks -> go blocks [] (return []) False
where
go :: [GenBasicBlock (LiveInstr instr)]
-> [GenBasicBlock (LiveInstr instr)]
-> [[NatBasicBlock instr]]
-> Bool
-> RegM freeRegs [[NatBasicBlock instr]]
go [] [] accum _madeProgress
= return $ reverse accum
go [] next_round accum madeProgress
| not madeProgress
= return $ reverse accum
| otherwise
= go next_round [] accum False
go (b@(BasicBlock id _) : blocks) next_round accum madeProgress
= do
block_assig <- getBlockAssigR
if isJust (mapLookup id block_assig) || id `elem` entry_ids
then do b' <- processBlock block_live b
go blocks next_round (b' : accum) True
else do go blocks (b : next_round) accum madeProgress
processBlock
:: OutputableRegConstraint freeRegs instr
=> BlockMap RegSet
-> LiveBasicBlock instr
-> RegM freeRegs [NatBasicBlock instr]
processBlock block_live (BasicBlock id instrs)
= do
initBlock id block_live
(instrs', fixups)
<- linearRA block_live id instrs
return $ BasicBlock id instrs' : fixups
initBlock :: FR freeRegs
=> BlockId -> BlockMap RegSet -> RegM freeRegs ()
initBlock id block_live
= do platform <- getPlatform
block_assig <- getBlockAssigR
case mapLookup id block_assig of
Nothing
-> do
case mapLookup id block_live of
Nothing ->
setFreeRegsR (frInitFreeRegs platform)
Just live ->
setFreeRegsR $ foldl' (flip $ frAllocateReg platform) (frInitFreeRegs platform)
[ r | RegReal r <- nonDetEltsUniqSet live ]
setAssigR emptyRegMap
Just (freeregs, assig)
-> do setFreeRegsR freeregs
setAssigR assig
linearRA
:: forall freeRegs instr. (OutputableRegConstraint freeRegs instr)
=> BlockMap RegSet
-> BlockId
-> [LiveInstr instr]
-> RegM freeRegs
( [instr]
, [NatBasicBlock instr])
linearRA block_live block_id = go [] []
where
go :: [instr]
-> [NatBasicBlock instr]
-> [LiveInstr instr]
-> RegM freeRegs
( [instr]
, [NatBasicBlock instr] )
go !accInstr !accFixups [] = do
return ( reverse accInstr
, accFixups )
go accInstr accFixups (instr:instrs) = do
(accInstr', new_fixups) <- raInsn block_live accInstr block_id instr
go accInstr' (new_fixups ++ accFixups) instrs
raInsn
:: OutputableRegConstraint freeRegs instr
=> 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@(Instr i) Nothing)
| isMetaInstr ii
= return (i : new_instrs, [])
raInsn block_live new_instrs id (LiveInstr (Instr instr) (Just live))
= do
assig <- getAssigR :: RegM freeRegs (UniqFM Reg Loc)
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 block_live new_instrs id instr
(nonDetEltsUniqSet $ liveDieRead live)
(nonDetEltsUniqSet $ liveDieWrite live)
raInsn _ _ _ instr
= do
platform <- getPlatform
let instr' = fmap (pprInstr platform) 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 :: forall freeRegs instr.
OutputableRegConstraint freeRegs instr
=> BlockMap RegSet
-> [instr]
-> BlockId
-> instr
-> [Reg]
-> [Reg]
-> RegM freeRegs ([instr], [NatBasicBlock instr])
genRaInsn block_live new_instrs block_id instr r_dying w_dying = do
platform <- getPlatform
case regUsageOfInstr platform instr of { RU read written ->
do
let real_written = [ rr | (RegReal rr) <- written ] :: [RealReg]
let virt_written = [ vr | (RegVirtual vr) <- written ]
let virt_read = nub [ vr | (RegVirtual vr) <- read ] :: [VirtualReg]
(r_spills, r_allocd) <-
allocateRegsAndSpill True virt_read [] [] virt_read
clobber_saves <- saveClobberedTemps real_written r_dying
(fixup_blocks, adjusted_instr)
<- joinToTargets block_live block_id instr
releaseRegs r_dying
clobberRegs real_written
(w_spills, w_allocd) <-
allocateRegsAndSpill False virt_written [] [] virt_written
releaseRegs w_dying
let
patch_map :: UniqFM Reg Reg
patch_map
= toRegMap $
listToUFM
[ (t, RegReal r)
| (t, r) <- zip virt_read r_allocd
++ zip virt_written w_allocd ]
patched_instr :: instr
patched_instr
= patchRegsOfInstr adjusted_instr patchLookup
patchLookup :: Reg -> Reg
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
platform <- getPlatform
assig <- getAssigR
free <- getFreeRegsR
let loop assig !free [] = do setAssigR assig; setFreeRegsR free; return ()
loop assig !free (RegReal rr : rs) = loop assig (frReleaseReg platform rr free) rs
loop assig !free (r:rs) =
case lookupUFM assig r of
Just (InBoth real _) -> loop (delFromUFM assig r)
(frReleaseReg platform real free) rs
Just (InReg real) -> loop (delFromUFM assig r)
(frReleaseReg platform real free) rs
_ -> loop (delFromUFM assig r) free rs
loop assig free regs
saveClobberedTemps
:: forall instr freeRegs.
(Instruction instr, FR freeRegs)
=> [RealReg]
-> [Reg]
-> RegM freeRegs [instr]
saveClobberedTemps [] _
= return []
saveClobberedTemps clobbered dying
= do
assig <- getAssigR :: RegM freeRegs (UniqFM Reg Loc)
let to_spill :: [(Unique, RealReg)]
to_spill
= [ (temp,reg)
| (temp, InReg reg) <- nonDetUFMToList assig
, any (realRegsAlias reg) clobbered
, temp `notElem` map getUnique dying ]
(instrs,assig') <- clobber assig [] to_spill
setAssigR assig'
return instrs
where
clobber :: RegMap Loc -> [instr] -> [(Unique,RealReg)] -> RegM freeRegs ([instr], RegMap Loc)
clobber assig instrs []
= return (instrs, assig)
clobber assig instrs ((temp, reg) : rest)
= do platform <- getPlatform
freeRegs <- getFreeRegsR
let regclass = targetClassOfRealReg platform reg
freeRegs_thisClass = frGetFreeRegs platform regclass freeRegs
case filter (`notElem` clobbered) freeRegs_thisClass of
(my_reg : _) -> do
setFreeRegsR (frAllocateReg platform my_reg freeRegs)
let new_assign = addToUFM_Directly assig temp (InReg my_reg)
let instr = mkRegRegMoveInstr platform
(RegReal reg) (RegReal my_reg)
clobber new_assign (instr : instrs) rest
[] -> do
(spill, slot) <- spillR (RegReal reg) temp
recordSpill (SpillClobber temp)
let new_assign = addToUFM_Directly assig temp (InBoth reg slot)
clobber new_assign (spill : instrs) rest
clobberRegs :: FR freeRegs => [RealReg] -> RegM freeRegs ()
clobberRegs []
= return ()
clobberRegs clobbered
= do platform <- getPlatform
freeregs <- getFreeRegsR
setFreeRegsR $! foldl' (flip $ frAllocateReg platform) freeregs clobbered
assig <- getAssigR
setAssigR $! clobber assig (nonDetUFMToList assig)
where
clobber :: RegMap Loc -> [(Unique,Loc)] -> RegMap Loc
clobber assig []
= assig
clobber assig ((temp, InBoth reg slot) : rest)
| any (realRegsAlias reg) clobbered
= clobber (addToUFM_Directly assig temp (InMem slot)) rest
clobber assig (_:rest)
= clobber assig rest
data SpillLoc = ReadMem StackSlot
| WriteNew
| WriteMem
allocateRegsAndSpill
:: forall freeRegs instr. (FR freeRegs, Instruction instr)
=> Bool
-> [VirtualReg]
-> [instr]
-> [RealReg]
-> [VirtualReg]
-> RegM freeRegs ( [instr] , [RealReg])
allocateRegsAndSpill _ _ spills alloc []
= return (spills, reverse alloc)
allocateRegsAndSpill reading keep spills alloc (r:rs)
= do assig <- toVRegMap <$> getAssigR
let doSpill = allocRegsAndSpill_spill reading keep spills alloc r rs assig
case lookupUFM assig r of
Just (InReg my_reg) ->
allocateRegsAndSpill reading keep spills (my_reg:alloc) rs
Just (InBoth my_reg _)
-> do when (not reading) (setAssigR $ toRegMap (addToUFM assig r (InReg my_reg)))
allocateRegsAndSpill reading keep spills (my_reg:alloc) rs
Just (InMem slot) | reading -> doSpill (ReadMem slot)
| otherwise -> doSpill WriteMem
Nothing | reading ->
pprPanic "allocateRegsAndSpill: Cannot read from uninitialized register" (ppr r)
| otherwise -> doSpill WriteNew
findPrefRealReg :: VirtualReg -> RegM freeRegs (Maybe RealReg)
findPrefRealReg vreg = do
bassig <- getBlockAssigR :: RegM freeRegs (BlockMap (freeRegs,RegMap Loc))
return $ foldr (findVirtRegAssig) Nothing bassig
where
findVirtRegAssig :: (freeRegs,RegMap Loc) -> Maybe RealReg -> Maybe RealReg
findVirtRegAssig assig z =
z <|> case lookupUFM (toVRegMap $ snd assig) vreg of
Just (InReg real_reg) -> Just real_reg
Just (InBoth real_reg _) -> Just real_reg
_ -> z
allocRegsAndSpill_spill :: (FR freeRegs, Instruction instr)
=> Bool
-> [VirtualReg]
-> [instr]
-> [RealReg]
-> VirtualReg
-> [VirtualReg]
-> UniqFM VirtualReg Loc
-> SpillLoc
-> RegM freeRegs ([instr], [RealReg])
allocRegsAndSpill_spill reading keep spills alloc r rs assig spill_loc
= do platform <- getPlatform
freeRegs <- getFreeRegsR
let freeRegs_thisClass = frGetFreeRegs platform (classOfVirtualReg r) freeRegs :: [RealReg]
pref_reg <- findPrefRealReg r
case freeRegs_thisClass of
(first_free : _) ->
do let !final_reg
| Just reg <- pref_reg
, reg `elem` freeRegs_thisClass
= reg
| otherwise
= first_free
spills' <- loadTemp r spill_loc final_reg spills
setAssigR $ toRegMap
$ (addToUFM assig r $! newLocation spill_loc final_reg)
setFreeRegsR $ frAllocateReg platform final_reg freeRegs
allocateRegsAndSpill reading keep spills' (final_reg : alloc) rs
[] ->
do let inRegOrBoth (InReg _) = True
inRegOrBoth (InBoth _ _) = True
inRegOrBoth _ = False
let candidates' :: UniqFM VirtualReg Loc
candidates' =
flip delListFromUFM keep $
filterUFM inRegOrBoth $
assig
let candidates = nonDetUFMToList candidates'
let candidates_inBoth :: [(Unique, RealReg, StackSlot)]
candidates_inBoth
= [ (temp, reg, mem)
| (temp, InBoth reg mem) <- candidates
, targetClassOfRealReg platform reg == classOfVirtualReg r ]
let candidates_inReg
= [ (temp, reg)
| (temp, InReg reg) <- candidates
, targetClassOfRealReg platform reg == classOfVirtualReg r ]
let result
| (temp, my_reg, slot) : _ <- candidates_inBoth
= do spills' <- loadTemp r spill_loc my_reg spills
let assig1 = addToUFM_Directly assig temp (InMem slot)
let assig2 = addToUFM assig1 r $! newLocation spill_loc my_reg
setAssigR $ toRegMap assig2
allocateRegsAndSpill reading keep spills' (my_reg:alloc) rs
| (temp_to_push_out, (my_reg :: RealReg)) : _
<- candidates_inReg
= do
(spill_insn, slot) <- spillR (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_Directly assig temp_to_push_out (InMem slot)
let assig2 = addToUFM assig1 r $! newLocation spill_loc my_reg
setAssigR $ toRegMap assig2
spills' <- loadTemp r spill_loc my_reg spills
allocateRegsAndSpill 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: " <> ppr assig
, text "freeRegs: " <> text (show freeRegs)
, text "initFreeRegs: " <> text (show (frInitFreeRegs platform `asTypeOf` freeRegs)) ]
result
newLocation :: SpillLoc -> RealReg -> Loc
newLocation (ReadMem slot) my_reg = InBoth my_reg slot
newLocation _ my_reg = InReg my_reg
loadTemp
:: (Instruction instr)
=> VirtualReg
-> SpillLoc
-> RealReg
-> [instr]
-> RegM freeRegs [instr]
loadTemp vreg (ReadMem slot) hreg spills
= do
insn <- loadR (RegReal hreg) slot
recordSpill (SpillLoad $ getUnique vreg)
return $ insn : spills
loadTemp _ _ _ spills =
return spills