PageRenderTime 74ms CodeModel.GetById 42ms RepoModel.GetById 0ms app.codeStats 0ms

/compiler/nativeGen/RegAlloc/Linear/Main.hs

https://github.com/alphaHeavy/ghc
Haskell | 817 lines | 428 code | 147 blank | 242 comment | 8 complexity | 5c2e7e5300f0f55dfc3885769467b5ec MD5 | raw file
  1. -----------------------------------------------------------------------------
  2. --
  3. -- The register allocator
  4. --
  5. -- (c) The University of Glasgow 2004
  6. --
  7. -----------------------------------------------------------------------------
  8. {-
  9. The algorithm is roughly:
  10. 1) Compute strongly connected components of the basic block list.
  11. 2) Compute liveness (mapping from pseudo register to
  12. point(s) of death?).
  13. 3) Walk instructions in each basic block. We keep track of
  14. (a) Free real registers (a bitmap?)
  15. (b) Current assignment of temporaries to machine registers and/or
  16. spill slots (call this the "assignment").
  17. (c) Partial mapping from basic block ids to a virt-to-loc mapping.
  18. When we first encounter a branch to a basic block,
  19. we fill in its entry in this table with the current mapping.
  20. For each instruction:
  21. (a) For each real register clobbered by this instruction:
  22. If a temporary resides in it,
  23. If the temporary is live after this instruction,
  24. Move the temporary to another (non-clobbered & free) reg,
  25. or spill it to memory. Mark the temporary as residing
  26. in both memory and a register if it was spilled (it might
  27. need to be read by this instruction).
  28. (ToDo: this is wrong for jump instructions?)
  29. (b) For each temporary *read* by the instruction:
  30. If the temporary does not have a real register allocation:
  31. - Allocate a real register from the free list. If
  32. the list is empty:
  33. - Find a temporary to spill. Pick one that is
  34. not used in this instruction (ToDo: not
  35. used for a while...)
  36. - generate a spill instruction
  37. - If the temporary was previously spilled,
  38. generate an instruction to read the temp from its spill loc.
  39. (optimisation: if we can see that a real register is going to
  40. be used soon, then don't use it for allocation).
  41. (c) Update the current assignment
  42. (d) If the instruction is a branch:
  43. if the destination block already has a register assignment,
  44. Generate a new block with fixup code and redirect the
  45. jump to the new block.
  46. else,
  47. Update the block id->assignment mapping with the current
  48. assignment.
  49. (e) Delete all register assignments for temps which are read
  50. (only) and die here. Update the free register list.
  51. (f) Mark all registers clobbered by this instruction as not free,
  52. and mark temporaries which have been spilled due to clobbering
  53. as in memory (step (a) marks then as in both mem & reg).
  54. (g) For each temporary *written* by this instruction:
  55. Allocate a real register as for (b), spilling something
  56. else if necessary.
  57. - except when updating the assignment, drop any memory
  58. locations that the temporary was previously in, since
  59. they will be no longer valid after this instruction.
  60. (h) Delete all register assignments for temps which are
  61. written and die here (there should rarely be any). Update
  62. the free register list.
  63. (i) Rewrite the instruction with the new mapping.
  64. (j) For each spilled reg known to be now dead, re-add its stack slot
  65. to the free list.
  66. -}
  67. module RegAlloc.Linear.Main (
  68. regAlloc,
  69. module RegAlloc.Linear.Base,
  70. module RegAlloc.Linear.Stats
  71. ) where
  72. #include "HsVersions.h"
  73. import RegAlloc.Linear.State
  74. import RegAlloc.Linear.Base
  75. import RegAlloc.Linear.StackMap
  76. import RegAlloc.Linear.FreeRegs
  77. import RegAlloc.Linear.Stats
  78. import RegAlloc.Linear.JoinToTargets
  79. import qualified RegAlloc.Linear.PPC.FreeRegs as PPC
  80. import qualified RegAlloc.Linear.SPARC.FreeRegs as SPARC
  81. import qualified RegAlloc.Linear.X86.FreeRegs as X86
  82. import TargetReg
  83. import RegAlloc.Liveness
  84. import Instruction
  85. import Reg
  86. import BlockId
  87. import OldCmm hiding (RegSet)
  88. import Digraph
  89. import DynFlags
  90. import Unique
  91. import UniqSet
  92. import UniqFM
  93. import UniqSupply
  94. import Outputable
  95. import Platform
  96. import Data.Maybe
  97. import Data.List
  98. import Control.Monad
  99. #include "../includes/stg/MachRegs.h"
  100. -- -----------------------------------------------------------------------------
  101. -- Top level of the register allocator
  102. -- Allocate registers
  103. regAlloc
  104. :: (PlatformOutputable instr, Instruction instr)
  105. => DynFlags
  106. -> LiveCmmDecl statics instr
  107. -> UniqSM (NatCmmDecl statics instr, Maybe RegAllocStats)
  108. regAlloc _ (CmmData sec d)
  109. = return
  110. ( CmmData sec d
  111. , Nothing )
  112. regAlloc _ (CmmProc (LiveInfo info _ _ _) lbl [])
  113. = return ( CmmProc info lbl (ListGraph [])
  114. , Nothing )
  115. regAlloc dflags (CmmProc static lbl sccs)
  116. | LiveInfo info (Just first_id) (Just block_live) _ <- static
  117. = do
  118. -- do register allocation on each component.
  119. (final_blocks, stats)
  120. <- linearRegAlloc dflags first_id block_live sccs
  121. -- make sure the block that was first in the input list
  122. -- stays at the front of the output
  123. let ((first':_), rest')
  124. = partition ((== first_id) . blockId) final_blocks
  125. return ( CmmProc info lbl (ListGraph (first' : rest'))
  126. , Just stats)
  127. -- bogus. to make non-exhaustive match warning go away.
  128. regAlloc _ (CmmProc _ _ _)
  129. = panic "RegAllocLinear.regAlloc: no match"
  130. -- -----------------------------------------------------------------------------
  131. -- Linear sweep to allocate registers
  132. -- | Do register allocation on some basic blocks.
  133. -- But be careful to allocate a block in an SCC only if it has
  134. -- an entry in the block map or it is the first block.
  135. --
  136. linearRegAlloc
  137. :: (PlatformOutputable instr, Instruction instr)
  138. => DynFlags
  139. -> BlockId -- ^ the first block
  140. -> BlockMap RegSet -- ^ live regs on entry to each basic block
  141. -> [SCC (LiveBasicBlock instr)] -- ^ instructions annotated with "deaths"
  142. -> UniqSM ([NatBasicBlock instr], RegAllocStats)
  143. linearRegAlloc dflags first_id block_live sccs
  144. = let platform = targetPlatform dflags
  145. in case platformArch platform of
  146. ArchX86 -> linearRegAlloc' platform (frInitFreeRegs :: X86.FreeRegs) first_id block_live sccs
  147. ArchX86_64 -> linearRegAlloc' platform (frInitFreeRegs :: X86.FreeRegs) first_id block_live sccs
  148. ArchSPARC -> linearRegAlloc' platform (frInitFreeRegs :: SPARC.FreeRegs) first_id block_live sccs
  149. ArchPPC -> linearRegAlloc' platform (frInitFreeRegs :: PPC.FreeRegs) first_id block_live sccs
  150. ArchARM _ _ -> panic "linearRegAlloc ArchARM"
  151. ArchPPC_64 -> panic "linearRegAlloc ArchPPC_64"
  152. ArchUnknown -> panic "linearRegAlloc ArchUnknown"
  153. linearRegAlloc'
  154. :: (FR freeRegs, PlatformOutputable instr, Instruction instr)
  155. => Platform
  156. -> freeRegs
  157. -> BlockId -- ^ the first block
  158. -> BlockMap RegSet -- ^ live regs on entry to each basic block
  159. -> [SCC (LiveBasicBlock instr)] -- ^ instructions annotated with "deaths"
  160. -> UniqSM ([NatBasicBlock instr], RegAllocStats)
  161. linearRegAlloc' platform initFreeRegs first_id block_live sccs
  162. = do us <- getUs
  163. let (_, _, stats, blocks) =
  164. runR emptyBlockMap initFreeRegs emptyRegMap (emptyStackMap platform) us
  165. $ linearRA_SCCs platform first_id block_live [] sccs
  166. return (blocks, stats)
  167. linearRA_SCCs :: (FR freeRegs, Instruction instr, PlatformOutputable instr)
  168. => Platform
  169. -> BlockId
  170. -> BlockMap RegSet
  171. -> [NatBasicBlock instr]
  172. -> [SCC (LiveBasicBlock instr)]
  173. -> RegM freeRegs [NatBasicBlock instr]
  174. linearRA_SCCs _ _ _ blocksAcc []
  175. = return $ reverse blocksAcc
  176. linearRA_SCCs platform first_id block_live blocksAcc (AcyclicSCC block : sccs)
  177. = do blocks' <- processBlock platform block_live block
  178. linearRA_SCCs platform first_id block_live
  179. ((reverse blocks') ++ blocksAcc)
  180. sccs
  181. linearRA_SCCs platform first_id block_live blocksAcc (CyclicSCC blocks : sccs)
  182. = do
  183. blockss' <- process platform first_id block_live blocks [] (return []) False
  184. linearRA_SCCs platform first_id block_live
  185. (reverse (concat blockss') ++ blocksAcc)
  186. sccs
  187. {- from John Dias's patch 2008/10/16:
  188. The linear-scan allocator sometimes allocates a block
  189. before allocating one of its predecessors, which could lead to
  190. inconsistent allocations. Make it so a block is only allocated
  191. if a predecessor has set the "incoming" assignments for the block, or
  192. if it's the procedure's entry block.
  193. BL 2009/02: Careful. If the assignment for a block doesn't get set for
  194. some reason then this function will loop. We should probably do some
  195. more sanity checking to guard against this eventuality.
  196. -}
  197. process :: (FR freeRegs, Instruction instr, PlatformOutputable instr)
  198. => Platform
  199. -> BlockId
  200. -> BlockMap RegSet
  201. -> [GenBasicBlock (LiveInstr instr)]
  202. -> [GenBasicBlock (LiveInstr instr)]
  203. -> [[NatBasicBlock instr]]
  204. -> Bool
  205. -> RegM freeRegs [[NatBasicBlock instr]]
  206. process _ _ _ [] [] accum _
  207. = return $ reverse accum
  208. process platform first_id block_live [] next_round accum madeProgress
  209. | not madeProgress
  210. {- BUGS: There are so many unreachable blocks in the code the warnings are overwhelming.
  211. pprTrace "RegAlloc.Linear.Main.process: no progress made, bailing out."
  212. ( text "Unreachable blocks:"
  213. $$ vcat (map ppr next_round)) -}
  214. = return $ reverse accum
  215. | otherwise
  216. = process platform first_id block_live
  217. next_round [] accum False
  218. process platform first_id block_live (b@(BasicBlock id _) : blocks)
  219. next_round accum madeProgress
  220. = do
  221. block_assig <- getBlockAssigR
  222. if isJust (mapLookup id block_assig)
  223. || id == first_id
  224. then do
  225. b' <- processBlock platform block_live b
  226. process platform first_id block_live blocks
  227. next_round (b' : accum) True
  228. else process platform first_id block_live blocks
  229. (b : next_round) accum madeProgress
  230. -- | Do register allocation on this basic block
  231. --
  232. processBlock
  233. :: (FR freeRegs, PlatformOutputable instr, Instruction instr)
  234. => Platform
  235. -> BlockMap RegSet -- ^ live regs on entry to each basic block
  236. -> LiveBasicBlock instr -- ^ block to do register allocation on
  237. -> RegM freeRegs [NatBasicBlock instr] -- ^ block with registers allocated
  238. processBlock platform block_live (BasicBlock id instrs)
  239. = do initBlock id
  240. (instrs', fixups)
  241. <- linearRA platform block_live [] [] id instrs
  242. return $ BasicBlock id instrs' : fixups
  243. -- | Load the freeregs and current reg assignment into the RegM state
  244. -- for the basic block with this BlockId.
  245. initBlock :: FR freeRegs => BlockId -> RegM freeRegs ()
  246. initBlock id
  247. = do block_assig <- getBlockAssigR
  248. case mapLookup id block_assig of
  249. -- no prior info about this block: assume everything is
  250. -- free and the assignment is empty.
  251. Nothing
  252. -> do -- pprTrace "initFreeRegs" (text $ show initFreeRegs) (return ())
  253. setFreeRegsR frInitFreeRegs
  254. setAssigR emptyRegMap
  255. -- load info about register assignments leading into this block.
  256. Just (freeregs, assig)
  257. -> do setFreeRegsR freeregs
  258. setAssigR assig
  259. -- | Do allocation for a sequence of instructions.
  260. linearRA
  261. :: (FR freeRegs, PlatformOutputable instr, Instruction instr)
  262. => Platform
  263. -> BlockMap RegSet -- ^ map of what vregs are live on entry to each block.
  264. -> [instr] -- ^ accumulator for instructions already processed.
  265. -> [NatBasicBlock instr] -- ^ accumulator for blocks of fixup code.
  266. -> BlockId -- ^ id of the current block, for debugging.
  267. -> [LiveInstr instr] -- ^ liveness annotated instructions in this block.
  268. -> RegM freeRegs
  269. ( [instr] -- instructions after register allocation
  270. , [NatBasicBlock instr]) -- fresh blocks of fixup code.
  271. linearRA _ _ accInstr accFixup _ []
  272. = return
  273. ( reverse accInstr -- instrs need to be returned in the correct order.
  274. , accFixup) -- it doesn't matter what order the fixup blocks are returned in.
  275. linearRA platform block_live accInstr accFixups id (instr:instrs)
  276. = do
  277. (accInstr', new_fixups)
  278. <- raInsn platform block_live accInstr id instr
  279. linearRA platform block_live accInstr' (new_fixups ++ accFixups) id instrs
  280. -- | Do allocation for a single instruction.
  281. raInsn
  282. :: (FR freeRegs, PlatformOutputable instr, Instruction instr)
  283. => Platform
  284. -> BlockMap RegSet -- ^ map of what vregs are love on entry to each block.
  285. -> [instr] -- ^ accumulator for instructions already processed.
  286. -> BlockId -- ^ the id of the current block, for debugging
  287. -> LiveInstr instr -- ^ the instr to have its regs allocated, with liveness info.
  288. -> RegM freeRegs
  289. ( [instr] -- new instructions
  290. , [NatBasicBlock instr]) -- extra fixup blocks
  291. raInsn _ _ new_instrs _ (LiveInstr ii Nothing)
  292. | Just n <- takeDeltaInstr ii
  293. = do setDeltaR n
  294. return (new_instrs, [])
  295. raInsn _ _ new_instrs _ (LiveInstr ii Nothing)
  296. | isMetaInstr ii
  297. = return (new_instrs, [])
  298. raInsn platform block_live new_instrs id (LiveInstr (Instr instr) (Just live))
  299. = do
  300. assig <- getAssigR
  301. -- If we have a reg->reg move between virtual registers, where the
  302. -- src register is not live after this instruction, and the dst
  303. -- register does not already have an assignment,
  304. -- and the source register is assigned to a register, not to a spill slot,
  305. -- then we can eliminate the instruction.
  306. -- (we can't eliminate it if the source register is on the stack, because
  307. -- we do not want to use one spill slot for different virtual registers)
  308. case takeRegRegMoveInstr instr of
  309. Just (src,dst) | src `elementOfUniqSet` (liveDieRead live),
  310. isVirtualReg dst,
  311. not (dst `elemUFM` assig),
  312. Just (InReg _) <- (lookupUFM assig src) -> do
  313. case src of
  314. (RegReal rr) -> setAssigR (addToUFM assig dst (InReg rr))
  315. -- if src is a fixed reg, then we just map dest to this
  316. -- reg in the assignment. src must be an allocatable reg,
  317. -- otherwise it wouldn't be in r_dying.
  318. _virt -> case lookupUFM assig src of
  319. Nothing -> panic "raInsn"
  320. Just loc ->
  321. setAssigR (addToUFM (delFromUFM assig src) dst loc)
  322. -- we have eliminated this instruction
  323. {-
  324. freeregs <- getFreeRegsR
  325. assig <- getAssigR
  326. pprTrace "raInsn" (text "ELIMINATED: " <> docToSDoc (pprInstr instr)
  327. $$ ppr r_dying <+> ppr w_dying $$ text (show freeregs) $$ ppr assig) $ do
  328. -}
  329. return (new_instrs, [])
  330. _ -> genRaInsn platform block_live new_instrs id instr
  331. (uniqSetToList $ liveDieRead live)
  332. (uniqSetToList $ liveDieWrite live)
  333. raInsn platform _ _ _ instr
  334. = pprPanic "raInsn" (text "no match for:" <> pprPlatform platform instr)
  335. genRaInsn :: (FR freeRegs, Instruction instr, PlatformOutputable instr)
  336. => Platform
  337. -> BlockMap RegSet
  338. -> [instr]
  339. -> BlockId
  340. -> instr
  341. -> [Reg]
  342. -> [Reg]
  343. -> RegM freeRegs ([instr], [NatBasicBlock instr])
  344. genRaInsn platform block_live new_instrs block_id instr r_dying w_dying =
  345. case regUsageOfInstr instr of { RU read written ->
  346. do
  347. let real_written = [ rr | (RegReal rr) <- written ]
  348. let virt_written = [ vr | (RegVirtual vr) <- written ]
  349. -- we don't need to do anything with real registers that are
  350. -- only read by this instr. (the list is typically ~2 elements,
  351. -- so using nub isn't a problem).
  352. let virt_read = nub [ vr | (RegVirtual vr) <- read ]
  353. -- (a) save any temporaries which will be clobbered by this instruction
  354. clobber_saves <- saveClobberedTemps platform real_written r_dying
  355. -- debugging
  356. {- freeregs <- getFreeRegsR
  357. assig <- getAssigR
  358. pprTrace "genRaInsn"
  359. (ppr instr
  360. $$ text "r_dying = " <+> ppr r_dying
  361. $$ text "w_dying = " <+> ppr w_dying
  362. $$ text "virt_read = " <+> ppr virt_read
  363. $$ text "virt_written = " <+> ppr virt_written
  364. $$ text "freeregs = " <+> text (show freeregs)
  365. $$ text "assig = " <+> ppr assig)
  366. $ do
  367. -}
  368. -- (b), (c) allocate real regs for all regs read by this instruction.
  369. (r_spills, r_allocd) <-
  370. allocateRegsAndSpill platform True{-reading-} virt_read [] [] virt_read
  371. -- (d) Update block map for new destinations
  372. -- NB. do this before removing dead regs from the assignment, because
  373. -- these dead regs might in fact be live in the jump targets (they're
  374. -- only dead in the code that follows in the current basic block).
  375. (fixup_blocks, adjusted_instr)
  376. <- joinToTargets platform block_live block_id instr
  377. -- (e) Delete all register assignments for temps which are read
  378. -- (only) and die here. Update the free register list.
  379. releaseRegs r_dying
  380. -- (f) Mark regs which are clobbered as unallocatable
  381. clobberRegs real_written
  382. -- (g) Allocate registers for temporaries *written* (only)
  383. (w_spills, w_allocd) <-
  384. allocateRegsAndSpill platform False{-writing-} virt_written [] [] virt_written
  385. -- (h) Release registers for temps which are written here and not
  386. -- used again.
  387. releaseRegs w_dying
  388. let
  389. -- (i) Patch the instruction
  390. patch_map
  391. = listToUFM
  392. [ (t, RegReal r)
  393. | (t, r) <- zip virt_read r_allocd
  394. ++ zip virt_written w_allocd ]
  395. patched_instr
  396. = patchRegsOfInstr adjusted_instr patchLookup
  397. patchLookup x
  398. = case lookupUFM patch_map x of
  399. Nothing -> x
  400. Just y -> y
  401. -- (j) free up stack slots for dead spilled regs
  402. -- TODO (can't be bothered right now)
  403. -- erase reg->reg moves where the source and destination are the same.
  404. -- If the src temp didn't die in this instr but happened to be allocated
  405. -- to the same real reg as the destination, then we can erase the move anyway.
  406. let squashed_instr = case takeRegRegMoveInstr patched_instr of
  407. Just (src, dst)
  408. | src == dst -> []
  409. _ -> [patched_instr]
  410. let code = squashed_instr ++ w_spills ++ reverse r_spills
  411. ++ clobber_saves ++ new_instrs
  412. -- pprTrace "patched-code" ((vcat $ map (docToSDoc . pprInstr) code)) $ do
  413. -- pprTrace "pached-fixup" ((ppr fixup_blocks)) $ do
  414. return (code, fixup_blocks)
  415. }
  416. -- -----------------------------------------------------------------------------
  417. -- releaseRegs
  418. releaseRegs :: FR freeRegs => [Reg] -> RegM freeRegs ()
  419. releaseRegs regs = do
  420. assig <- getAssigR
  421. free <- getFreeRegsR
  422. loop assig free regs
  423. where
  424. loop _ free _ | free `seq` False = undefined
  425. loop assig free [] = do setAssigR assig; setFreeRegsR free; return ()
  426. loop assig free (RegReal rr : rs) = loop assig (frReleaseReg rr free) rs
  427. loop assig free (r:rs) =
  428. case lookupUFM assig r of
  429. Just (InBoth real _) -> loop (delFromUFM assig r) (frReleaseReg real free) rs
  430. Just (InReg real) -> loop (delFromUFM assig r) (frReleaseReg real free) rs
  431. _other -> loop (delFromUFM assig r) free rs
  432. -- -----------------------------------------------------------------------------
  433. -- Clobber real registers
  434. -- For each temp in a register that is going to be clobbered:
  435. -- - if the temp dies after this instruction, do nothing
  436. -- - otherwise, put it somewhere safe (another reg if possible,
  437. -- otherwise spill and record InBoth in the assignment).
  438. -- - for allocateRegs on the temps *read*,
  439. -- - clobbered regs are allocatable.
  440. --
  441. -- for allocateRegs on the temps *written*,
  442. -- - clobbered regs are not allocatable.
  443. --
  444. -- TODO: instead of spilling, try to copy clobbered
  445. -- temps to another register if possible.
  446. --
  447. saveClobberedTemps
  448. :: (PlatformOutputable instr, Instruction instr)
  449. => Platform
  450. -> [RealReg] -- real registers clobbered by this instruction
  451. -> [Reg] -- registers which are no longer live after this insn
  452. -> RegM freeRegs [instr] -- return: instructions to spill any temps that will
  453. -- be clobbered.
  454. saveClobberedTemps _ [] _
  455. = return []
  456. saveClobberedTemps platform clobbered dying
  457. = do
  458. assig <- getAssigR
  459. let to_spill
  460. = [ (temp,reg)
  461. | (temp, InReg reg) <- ufmToList assig
  462. , any (realRegsAlias reg) clobbered
  463. , temp `notElem` map getUnique dying ]
  464. (instrs,assig') <- clobber assig [] to_spill
  465. setAssigR assig'
  466. return instrs
  467. where
  468. clobber assig instrs []
  469. = return (instrs, assig)
  470. clobber assig instrs ((temp, reg) : rest)
  471. = do
  472. (spill, slot) <- spillR platform (RegReal reg) temp
  473. -- record why this reg was spilled for profiling
  474. recordSpill (SpillClobber temp)
  475. let new_assign = addToUFM assig temp (InBoth reg slot)
  476. clobber new_assign (spill : instrs) rest
  477. -- | Mark all these real regs as allocated,
  478. -- and kick out their vreg assignments.
  479. --
  480. clobberRegs :: FR freeRegs => [RealReg] -> RegM freeRegs ()
  481. clobberRegs []
  482. = return ()
  483. clobberRegs clobbered
  484. = do
  485. freeregs <- getFreeRegsR
  486. setFreeRegsR $! foldr frAllocateReg freeregs clobbered
  487. assig <- getAssigR
  488. setAssigR $! clobber assig (ufmToList assig)
  489. where
  490. -- if the temp was InReg and clobbered, then we will have
  491. -- saved it in saveClobberedTemps above. So the only case
  492. -- we have to worry about here is InBoth. Note that this
  493. -- also catches temps which were loaded up during allocation
  494. -- of read registers, not just those saved in saveClobberedTemps.
  495. clobber assig []
  496. = assig
  497. clobber assig ((temp, InBoth reg slot) : rest)
  498. | any (realRegsAlias reg) clobbered
  499. = clobber (addToUFM assig temp (InMem slot)) rest
  500. clobber assig (_:rest)
  501. = clobber assig rest
  502. -- -----------------------------------------------------------------------------
  503. -- allocateRegsAndSpill
  504. -- Why are we performing a spill?
  505. data SpillLoc = ReadMem StackSlot -- reading from register only in memory
  506. | WriteNew -- writing to a new variable
  507. | WriteMem -- writing to register only in memory
  508. -- Note that ReadNew is not valid, since you don't want to be reading
  509. -- from an uninitialized register. We also don't need the location of
  510. -- the register in memory, since that will be invalidated by the write.
  511. -- Technically, we could coalesce WriteNew and WriteMem into a single
  512. -- entry as well. -- EZY
  513. -- This function does several things:
  514. -- For each temporary referred to by this instruction,
  515. -- we allocate a real register (spilling another temporary if necessary).
  516. -- We load the temporary up from memory if necessary.
  517. -- We also update the register assignment in the process, and
  518. -- the list of free registers and free stack slots.
  519. allocateRegsAndSpill
  520. :: (FR freeRegs, PlatformOutputable instr, Instruction instr)
  521. => Platform
  522. -> Bool -- True <=> reading (load up spilled regs)
  523. -> [VirtualReg] -- don't push these out
  524. -> [instr] -- spill insns
  525. -> [RealReg] -- real registers allocated (accum.)
  526. -> [VirtualReg] -- temps to allocate
  527. -> RegM freeRegs ( [instr] , [RealReg])
  528. allocateRegsAndSpill _ _ _ spills alloc []
  529. = return (spills, reverse alloc)
  530. allocateRegsAndSpill platform reading keep spills alloc (r:rs)
  531. = do assig <- getAssigR
  532. let doSpill = allocRegsAndSpill_spill platform reading keep spills alloc r rs assig
  533. case lookupUFM assig r of
  534. -- case (1a): already in a register
  535. Just (InReg my_reg) ->
  536. allocateRegsAndSpill platform reading keep spills (my_reg:alloc) rs
  537. -- case (1b): already in a register (and memory)
  538. -- NB1. if we're writing this register, update its assignment to be
  539. -- InReg, because the memory value is no longer valid.
  540. -- NB2. This is why we must process written registers here, even if they
  541. -- are also read by the same instruction.
  542. Just (InBoth my_reg _)
  543. -> do when (not reading) (setAssigR (addToUFM assig r (InReg my_reg)))
  544. allocateRegsAndSpill platform reading keep spills (my_reg:alloc) rs
  545. -- Not already in a register, so we need to find a free one...
  546. Just (InMem slot) | reading -> doSpill (ReadMem slot)
  547. | otherwise -> doSpill WriteMem
  548. Nothing | reading ->
  549. -- pprPanic "allocateRegsAndSpill: Cannot read from uninitialized register" (ppr r)
  550. -- ToDo: This case should be a panic, but we
  551. -- sometimes see an unreachable basic block which
  552. -- triggers this because the register allocator
  553. -- will start with an empty assignment.
  554. doSpill WriteNew
  555. | otherwise -> doSpill WriteNew
  556. -- reading is redundant with reason, but we keep it around because it's
  557. -- convenient and it maintains the recursive structure of the allocator. -- EZY
  558. allocRegsAndSpill_spill :: (FR freeRegs, Instruction instr, PlatformOutputable instr)
  559. => Platform
  560. -> Bool
  561. -> [VirtualReg]
  562. -> [instr]
  563. -> [RealReg]
  564. -> VirtualReg
  565. -> [VirtualReg]
  566. -> UniqFM Loc
  567. -> SpillLoc
  568. -> RegM freeRegs ([instr], [RealReg])
  569. allocRegsAndSpill_spill platform reading keep spills alloc r rs assig spill_loc
  570. = do
  571. freeRegs <- getFreeRegsR
  572. let freeRegs_thisClass = frGetFreeRegs (classOfVirtualReg r) freeRegs
  573. case freeRegs_thisClass of
  574. -- case (2): we have a free register
  575. (my_reg : _) ->
  576. do spills' <- loadTemp platform r spill_loc my_reg spills
  577. setAssigR (addToUFM assig r $! newLocation spill_loc my_reg)
  578. setFreeRegsR $ frAllocateReg my_reg freeRegs
  579. allocateRegsAndSpill platform reading keep spills' (my_reg : alloc) rs
  580. -- case (3): we need to push something out to free up a register
  581. [] ->
  582. do let keep' = map getUnique keep
  583. -- the vregs we could kick out that are already in a slot
  584. let candidates_inBoth
  585. = [ (temp, reg, mem)
  586. | (temp, InBoth reg mem) <- ufmToList assig
  587. , temp `notElem` keep'
  588. , targetClassOfRealReg platform reg == classOfVirtualReg r ]
  589. -- the vregs we could kick out that are only in a reg
  590. -- this would require writing the reg to a new slot before using it.
  591. let candidates_inReg
  592. = [ (temp, reg)
  593. | (temp, InReg reg) <- ufmToList assig
  594. , temp `notElem` keep'
  595. , targetClassOfRealReg platform reg == classOfVirtualReg r ]
  596. let result
  597. -- we have a temporary that is in both register and mem,
  598. -- just free up its register for use.
  599. | (temp, my_reg, slot) : _ <- candidates_inBoth
  600. = do spills' <- loadTemp platform r spill_loc my_reg spills
  601. let assig1 = addToUFM assig temp (InMem slot)
  602. let assig2 = addToUFM assig1 r $! newLocation spill_loc my_reg
  603. setAssigR assig2
  604. allocateRegsAndSpill platform reading keep spills' (my_reg:alloc) rs
  605. -- otherwise, we need to spill a temporary that currently
  606. -- resides in a register.
  607. | (temp_to_push_out, (my_reg :: RealReg)) : _
  608. <- candidates_inReg
  609. = do
  610. (spill_insn, slot) <- spillR platform (RegReal my_reg) temp_to_push_out
  611. let spill_store = (if reading then id else reverse)
  612. [ -- COMMENT (fsLit "spill alloc")
  613. spill_insn ]
  614. -- record that this temp was spilled
  615. recordSpill (SpillAlloc temp_to_push_out)
  616. -- update the register assignment
  617. let assig1 = addToUFM assig temp_to_push_out (InMem slot)
  618. let assig2 = addToUFM assig1 r $! newLocation spill_loc my_reg
  619. setAssigR assig2
  620. -- if need be, load up a spilled temp into the reg we've just freed up.
  621. spills' <- loadTemp platform r spill_loc my_reg spills
  622. allocateRegsAndSpill platform reading keep
  623. (spill_store ++ spills')
  624. (my_reg:alloc) rs
  625. -- there wasn't anything to spill, so we're screwed.
  626. | otherwise
  627. = pprPanic ("RegAllocLinear.allocRegsAndSpill: no spill candidates\n")
  628. $ vcat
  629. [ text "allocating vreg: " <> text (show r)
  630. , text "assignment: " <> text (show $ ufmToList assig)
  631. , text "freeRegs: " <> text (show freeRegs)
  632. , text "initFreeRegs: " <> text (show (frInitFreeRegs `asTypeOf` freeRegs)) ]
  633. result
  634. -- | Calculate a new location after a register has been loaded.
  635. newLocation :: SpillLoc -> RealReg -> Loc
  636. -- if the tmp was read from a slot, then now its in a reg as well
  637. newLocation (ReadMem slot) my_reg = InBoth my_reg slot
  638. -- writes will always result in only the register being available
  639. newLocation _ my_reg = InReg my_reg
  640. -- | Load up a spilled temporary if we need to (read from memory).
  641. loadTemp
  642. :: (PlatformOutputable instr, Instruction instr)
  643. => Platform
  644. -> VirtualReg -- the temp being loaded
  645. -> SpillLoc -- the current location of this temp
  646. -> RealReg -- the hreg to load the temp into
  647. -> [instr]
  648. -> RegM freeRegs [instr]
  649. loadTemp platform vreg (ReadMem slot) hreg spills
  650. = do
  651. insn <- loadR platform (RegReal hreg) slot
  652. recordSpill (SpillLoad $ getUnique vreg)
  653. return $ {- COMMENT (fsLit "spill load") : -} insn : spills
  654. loadTemp _ _ _ _ spills =
  655. return spills