PageRenderTime 60ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

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

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