PageRenderTime 74ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

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

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