PageRenderTime 71ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 1ms

/compiler/nativeGen/RegAlloc/Graph/Spill.hs

https://bitbucket.org/khibino/ghc-hack
Haskell | 338 lines | 215 code | 71 blank | 52 comment | 4 complexity | 361ca56bed8cab1ab1bd5fe94baac4f9 MD5 | raw file
Possible License(s): BSD-3-Clause, BSD-2-Clause, LGPL-3.0
  1. -- | When there aren't enough registers to hold all the vregs we have to spill some of those
  2. -- vregs to slots on the stack. This module is used modify the code to use those slots.
  3. --
  4. module RegAlloc.Graph.Spill (
  5. regSpill,
  6. SpillStats(..),
  7. accSpillSL
  8. )
  9. where
  10. import RegAlloc.Liveness
  11. import Instruction
  12. import Reg
  13. import OldCmm hiding (RegSet)
  14. import BlockId
  15. import State
  16. import Unique
  17. import UniqFM
  18. import UniqSet
  19. import UniqSupply
  20. import Outputable
  21. import Data.List
  22. import Data.Maybe
  23. import Data.Map (Map)
  24. import Data.Set (Set)
  25. import qualified Data.Map as Map
  26. import qualified Data.Set as Set
  27. -- | Spill all these virtual regs to stack slots.
  28. --
  29. -- TODO: See if we can split some of the live ranges instead of just globally
  30. -- spilling the virtual reg. This might make the spill cleaner's job easier.
  31. --
  32. -- TODO: On CISCy x86 and x86_64 we don't nessesarally have to add a mov instruction
  33. -- when making spills. If an instr is using a spilled virtual we may be able to
  34. -- address the spill slot directly.
  35. --
  36. regSpill
  37. :: Instruction instr
  38. => [LiveCmmDecl statics instr] -- ^ the code
  39. -> UniqSet Int -- ^ available stack slots
  40. -> UniqSet VirtualReg -- ^ the regs to spill
  41. -> UniqSM
  42. ([LiveCmmDecl statics instr] -- code with SPILL and RELOAD meta instructions added.
  43. , UniqSet Int -- left over slots
  44. , SpillStats ) -- stats about what happened during spilling
  45. regSpill code slotsFree regs
  46. -- not enough slots to spill these regs
  47. | sizeUniqSet slotsFree < sizeUniqSet regs
  48. = pprPanic "regSpill: out of spill slots!"
  49. ( text " regs to spill = " <> ppr (sizeUniqSet regs)
  50. $$ text " slots left = " <> ppr (sizeUniqSet slotsFree))
  51. | otherwise
  52. = do
  53. -- allocate a slot for each of the spilled regs
  54. let slots = take (sizeUniqSet regs) $ uniqSetToList slotsFree
  55. let regSlotMap = listToUFM
  56. $ zip (uniqSetToList regs) slots
  57. -- grab the unique supply from the monad
  58. us <- getUs
  59. -- run the spiller on all the blocks
  60. let (code', state') =
  61. runState (mapM (regSpill_top regSlotMap) code)
  62. (initSpillS us)
  63. return ( code'
  64. , minusUniqSet slotsFree (mkUniqSet slots)
  65. , makeSpillStats state')
  66. -- | Spill some registers to stack slots in a top-level thing.
  67. regSpill_top
  68. :: Instruction instr
  69. => RegMap Int -- ^ map of vregs to slots they're being spilled to.
  70. -> LiveCmmDecl statics instr -- ^ the top level thing.
  71. -> SpillM (LiveCmmDecl statics instr)
  72. regSpill_top regSlotMap cmm
  73. = case cmm of
  74. CmmData{}
  75. -> return cmm
  76. CmmProc info label sccs
  77. | LiveInfo static firstId mLiveVRegsOnEntry liveSlotsOnEntry <- info
  78. -> do
  79. -- We should only passed Cmms with the liveness maps filled in, but we'll
  80. -- create empty ones if they're not there just in case.
  81. let liveVRegsOnEntry = fromMaybe mapEmpty mLiveVRegsOnEntry
  82. -- The liveVRegsOnEntry contains the set of vregs that are live on entry to
  83. -- each basic block. If we spill one of those vregs we remove it from that
  84. -- set and add the corresponding slot number to the liveSlotsOnEntry set.
  85. -- The spill cleaner needs this information to erase unneeded spill and
  86. -- reload instructions after we've done a successful allocation.
  87. let liveSlotsOnEntry' :: Map BlockId (Set Int)
  88. liveSlotsOnEntry'
  89. = mapFoldWithKey patchLiveSlot liveSlotsOnEntry liveVRegsOnEntry
  90. let info'
  91. = LiveInfo static firstId
  92. (Just liveVRegsOnEntry)
  93. liveSlotsOnEntry'
  94. -- Apply the spiller to all the basic blocks in the CmmProc.
  95. sccs' <- mapM (mapSCCM (regSpill_block regSlotMap)) sccs
  96. return $ CmmProc info' label sccs'
  97. where -- | Given a BlockId and the set of registers live in it,
  98. -- if registers in this block are being spilled to stack slots,
  99. -- then record the fact that these slots are now live in those blocks
  100. -- in the given slotmap.
  101. patchLiveSlot :: BlockId -> RegSet -> Map BlockId (Set Int) -> Map BlockId (Set Int)
  102. patchLiveSlot blockId regsLive slotMap
  103. = let curSlotsLive = fromMaybe Set.empty
  104. $ Map.lookup blockId slotMap
  105. moreSlotsLive = Set.fromList
  106. $ catMaybes
  107. $ map (lookupUFM regSlotMap)
  108. $ uniqSetToList regsLive
  109. slotMap' = Map.insert blockId (Set.union curSlotsLive moreSlotsLive) slotMap
  110. in slotMap'
  111. -- | Spill some registers to stack slots in a basic block.
  112. regSpill_block
  113. :: Instruction instr
  114. => UniqFM Int -- ^ map of vregs to slots they're being spilled to.
  115. -> LiveBasicBlock instr
  116. -> SpillM (LiveBasicBlock instr)
  117. regSpill_block regSlotMap (BasicBlock i instrs)
  118. = do instrss' <- mapM (regSpill_instr regSlotMap) instrs
  119. return $ BasicBlock i (concat instrss')
  120. -- | Spill some registers to stack slots in a single instruction. If the instruction
  121. -- uses registers that need to be spilled, then it is prefixed (or postfixed) with
  122. -- the appropriate RELOAD or SPILL meta instructions.
  123. regSpill_instr
  124. :: Instruction instr
  125. => UniqFM Int -- ^ map of vregs to slots they're being spilled to.
  126. -> LiveInstr instr
  127. -> SpillM [LiveInstr instr]
  128. regSpill_instr _ li@(LiveInstr _ Nothing)
  129. = do return [li]
  130. regSpill_instr regSlotMap
  131. (LiveInstr instr (Just _))
  132. = do
  133. -- work out which regs are read and written in this instr
  134. let RU rlRead rlWritten = regUsageOfInstr instr
  135. -- sometimes a register is listed as being read more than once,
  136. -- nub this so we don't end up inserting two lots of spill code.
  137. let rsRead_ = nub rlRead
  138. let rsWritten_ = nub rlWritten
  139. -- if a reg is modified, it appears in both lists, want to undo this..
  140. let rsRead = rsRead_ \\ rsWritten_
  141. let rsWritten = rsWritten_ \\ rsRead_
  142. let rsModify = intersect rsRead_ rsWritten_
  143. -- work out if any of the regs being used are currently being spilled.
  144. let rsSpillRead = filter (\r -> elemUFM r regSlotMap) rsRead
  145. let rsSpillWritten = filter (\r -> elemUFM r regSlotMap) rsWritten
  146. let rsSpillModify = filter (\r -> elemUFM r regSlotMap) rsModify
  147. -- rewrite the instr and work out spill code.
  148. (instr1, prepost1) <- mapAccumLM (spillRead regSlotMap) instr rsSpillRead
  149. (instr2, prepost2) <- mapAccumLM (spillWrite regSlotMap) instr1 rsSpillWritten
  150. (instr3, prepost3) <- mapAccumLM (spillModify regSlotMap) instr2 rsSpillModify
  151. let (mPrefixes, mPostfixes) = unzip (prepost1 ++ prepost2 ++ prepost3)
  152. let prefixes = concat mPrefixes
  153. let postfixes = concat mPostfixes
  154. -- final code
  155. let instrs' = prefixes
  156. ++ [LiveInstr instr3 Nothing]
  157. ++ postfixes
  158. return
  159. {- $ pprTrace "* regSpill_instr spill"
  160. ( text "instr = " <> ppr instr
  161. $$ text "read = " <> ppr rsSpillRead
  162. $$ text "write = " <> ppr rsSpillWritten
  163. $$ text "mod = " <> ppr rsSpillModify
  164. $$ text "-- out"
  165. $$ (vcat $ map ppr instrs')
  166. $$ text " ")
  167. -}
  168. $ instrs'
  169. spillRead
  170. :: Instruction instr
  171. => UniqFM Int
  172. -> instr
  173. -> Reg
  174. -> SpillM (instr, ([LiveInstr instr'], [LiveInstr instr']))
  175. spillRead regSlotMap instr reg
  176. | Just slot <- lookupUFM regSlotMap reg
  177. = do (instr', nReg) <- patchInstr reg instr
  178. modify $ \s -> s
  179. { stateSpillSL = addToUFM_C accSpillSL (stateSpillSL s) reg (reg, 0, 1) }
  180. return ( instr'
  181. , ( [LiveInstr (RELOAD slot nReg) Nothing]
  182. , []) )
  183. | otherwise = panic "RegSpill.spillRead: no slot defined for spilled reg"
  184. spillWrite
  185. :: Instruction instr
  186. => UniqFM Int
  187. -> instr
  188. -> Reg
  189. -> SpillM (instr, ([LiveInstr instr'], [LiveInstr instr']))
  190. spillWrite regSlotMap instr reg
  191. | Just slot <- lookupUFM regSlotMap reg
  192. = do (instr', nReg) <- patchInstr reg instr
  193. modify $ \s -> s
  194. { stateSpillSL = addToUFM_C accSpillSL (stateSpillSL s) reg (reg, 1, 0) }
  195. return ( instr'
  196. , ( []
  197. , [LiveInstr (SPILL nReg slot) Nothing]))
  198. | otherwise = panic "RegSpill.spillWrite: no slot defined for spilled reg"
  199. spillModify
  200. :: Instruction instr
  201. => UniqFM Int
  202. -> instr
  203. -> Reg
  204. -> SpillM (instr, ([LiveInstr instr'], [LiveInstr instr']))
  205. spillModify regSlotMap instr reg
  206. | Just slot <- lookupUFM regSlotMap reg
  207. = do (instr', nReg) <- patchInstr reg instr
  208. modify $ \s -> s
  209. { stateSpillSL = addToUFM_C accSpillSL (stateSpillSL s) reg (reg, 1, 1) }
  210. return ( instr'
  211. , ( [LiveInstr (RELOAD slot nReg) Nothing]
  212. , [LiveInstr (SPILL nReg slot) Nothing]))
  213. | otherwise = panic "RegSpill.spillModify: no slot defined for spilled reg"
  214. -- | Rewrite uses of this virtual reg in an instr to use a different virtual reg
  215. patchInstr
  216. :: Instruction instr
  217. => Reg -> instr -> SpillM (instr, Reg)
  218. patchInstr reg instr
  219. = do nUnique <- newUnique
  220. let nReg = case reg of
  221. RegVirtual vr -> RegVirtual (renameVirtualReg nUnique vr)
  222. RegReal{} -> panic "RegAlloc.Graph.Spill.patchIntr: not patching real reg"
  223. let instr' = patchReg1 reg nReg instr
  224. return (instr', nReg)
  225. patchReg1
  226. :: Instruction instr
  227. => Reg -> Reg -> instr -> instr
  228. patchReg1 old new instr
  229. = let patchF r
  230. | r == old = new
  231. | otherwise = r
  232. in patchRegsOfInstr instr patchF
  233. -- Spiller monad --------------------------------------------------------------
  234. data SpillS
  235. = SpillS
  236. { -- | unique supply for generating fresh vregs.
  237. stateUS :: UniqSupply
  238. -- | spilled vreg vs the number of times it was loaded, stored
  239. , stateSpillSL :: UniqFM (Reg, Int, Int) }
  240. initSpillS :: UniqSupply -> SpillS
  241. initSpillS uniqueSupply
  242. = SpillS
  243. { stateUS = uniqueSupply
  244. , stateSpillSL = emptyUFM }
  245. type SpillM a = State SpillS a
  246. newUnique :: SpillM Unique
  247. newUnique
  248. = do us <- gets stateUS
  249. case takeUniqFromSupply us of
  250. (uniq, us')
  251. -> do modify $ \s -> s { stateUS = us' }
  252. return uniq
  253. accSpillSL :: (Reg, Int, Int) -> (Reg, Int, Int) -> (Reg, Int, Int)
  254. accSpillSL (r1, s1, l1) (_, s2, l2)
  255. = (r1, s1 + s2, l1 + l2)
  256. -- Spiller stats --------------------------------------------------------------
  257. data SpillStats
  258. = SpillStats
  259. { spillStoreLoad :: UniqFM (Reg, Int, Int) }
  260. makeSpillStats :: SpillS -> SpillStats
  261. makeSpillStats s
  262. = SpillStats
  263. { spillStoreLoad = stateSpillSL s }
  264. instance Outputable SpillStats where
  265. ppr stats
  266. = (vcat $ map (\(r, s, l) -> ppr r <+> int s <+> int l)
  267. $ eltsUFM (spillStoreLoad stats))