PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/compiler/nativeGen/RegAlloc/Graph/SpillClean.hs

http://github.com/ghc/ghc
Haskell | 611 lines | 327 code | 131 blank | 153 comment | 9 complexity | c0c0e9dd328a7303bad7f93a25184b76 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, GPL-3.0
  1. -- | Clean out unneeded spill\/reload instructions.
  2. --
  3. -- Handling of join points
  4. -- ~~~~~~~~~~~~~~~~~~~~~~~
  5. --
  6. -- B1: B2:
  7. -- ... ...
  8. -- RELOAD SLOT(0), %r1 RELOAD SLOT(0), %r1
  9. -- ... A ... ... B ...
  10. -- jump B3 jump B3
  11. --
  12. -- B3: ... C ...
  13. -- RELOAD SLOT(0), %r1
  14. -- ...
  15. --
  16. -- The Plan
  17. -- ~~~~~~~~
  18. -- As long as %r1 hasn't been written to in A, B or C then we don't need
  19. -- the reload in B3.
  20. --
  21. -- What we really care about here is that on the entry to B3, %r1 will
  22. -- always have the same value that is in SLOT(0) (ie, %r1 is _valid_)
  23. --
  24. -- This also works if the reloads in B1\/B2 were spills instead, because
  25. -- spilling %r1 to a slot makes that slot have the same value as %r1.
  26. --
  27. module RegAlloc.Graph.SpillClean (
  28. cleanSpills
  29. ) where
  30. import RegAlloc.Liveness
  31. import Instruction
  32. import Reg
  33. import BlockId
  34. import Cmm
  35. import UniqSet
  36. import UniqFM
  37. import Unique
  38. import State
  39. import Outputable
  40. import Platform
  41. import Data.List
  42. import Data.Maybe
  43. import Data.IntSet (IntSet)
  44. import qualified Data.IntSet as IntSet
  45. -- | The identification number of a spill slot.
  46. -- A value is stored in a spill slot when we don't have a free
  47. -- register to hold it.
  48. type Slot = Int
  49. -- | Clean out unneeded spill\/reloads from this top level thing.
  50. cleanSpills
  51. :: Instruction instr
  52. => Platform
  53. -> LiveCmmDecl statics instr
  54. -> LiveCmmDecl statics instr
  55. cleanSpills platform cmm
  56. = evalState (cleanSpin platform 0 cmm) initCleanS
  57. -- | Do one pass of cleaning.
  58. cleanSpin
  59. :: Instruction instr
  60. => Platform
  61. -> Int -- ^ Iteration number for the cleaner.
  62. -> LiveCmmDecl statics instr -- ^ Liveness annotated code to clean.
  63. -> CleanM (LiveCmmDecl statics instr)
  64. cleanSpin platform spinCount code
  65. = do
  66. -- Initialise count of cleaned spill and reload instructions.
  67. modify $ \s -> s
  68. { sCleanedSpillsAcc = 0
  69. , sCleanedReloadsAcc = 0
  70. , sReloadedBy = emptyUFM }
  71. code_forward <- mapBlockTopM (cleanBlockForward platform) code
  72. code_backward <- cleanTopBackward code_forward
  73. -- During the cleaning of each block we collected information about
  74. -- what regs were valid across each jump. Based on this, work out
  75. -- whether it will be safe to erase reloads after join points for
  76. -- the next pass.
  77. collateJoinPoints
  78. -- Remember how many spill and reload instructions we cleaned in this pass.
  79. spills <- gets sCleanedSpillsAcc
  80. reloads <- gets sCleanedReloadsAcc
  81. modify $ \s -> s
  82. { sCleanedCount = (spills, reloads) : sCleanedCount s }
  83. -- If nothing was cleaned in this pass or the last one
  84. -- then we're done and it's time to bail out.
  85. cleanedCount <- gets sCleanedCount
  86. if take 2 cleanedCount == [(0, 0), (0, 0)]
  87. then return code
  88. -- otherwise go around again
  89. else cleanSpin platform (spinCount + 1) code_backward
  90. -------------------------------------------------------------------------------
  91. -- | Clean out unneeded reload instructions,
  92. -- while walking forward over the code.
  93. cleanBlockForward
  94. :: Instruction instr
  95. => Platform
  96. -> LiveBasicBlock instr
  97. -> CleanM (LiveBasicBlock instr)
  98. cleanBlockForward platform (BasicBlock blockId instrs)
  99. = do
  100. -- See if we have a valid association for the entry to this block.
  101. jumpValid <- gets sJumpValid
  102. let assoc = case lookupUFM jumpValid blockId of
  103. Just assoc -> assoc
  104. Nothing -> emptyAssoc
  105. instrs_reload <- cleanForward platform blockId assoc [] instrs
  106. return $ BasicBlock blockId instrs_reload
  107. -- | Clean out unneeded reload instructions.
  108. --
  109. -- Walking forwards across the code
  110. -- On a reload, if we know a reg already has the same value as a slot
  111. -- then we don't need to do the reload.
  112. --
  113. cleanForward
  114. :: Instruction instr
  115. => Platform
  116. -> BlockId -- ^ the block that we're currently in
  117. -> Assoc Store -- ^ two store locations are associated if
  118. -- they have the same value
  119. -> [LiveInstr instr] -- ^ acc
  120. -> [LiveInstr instr] -- ^ instrs to clean (in backwards order)
  121. -> CleanM [LiveInstr instr] -- ^ cleaned instrs (in forward order)
  122. cleanForward _ _ _ acc []
  123. = return acc
  124. -- Rewrite live range joins via spill slots to just a spill and a reg-reg move
  125. -- hopefully the spill will be also be cleaned in the next pass
  126. cleanForward platform blockId assoc acc (li1 : li2 : instrs)
  127. | LiveInstr (SPILL reg1 slot1) _ <- li1
  128. , LiveInstr (RELOAD slot2 reg2) _ <- li2
  129. , slot1 == slot2
  130. = do
  131. modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
  132. cleanForward platform blockId assoc acc
  133. $ li1 : LiveInstr (mkRegRegMoveInstr platform reg1 reg2) Nothing
  134. : instrs
  135. cleanForward platform blockId assoc acc (li@(LiveInstr i1 _) : instrs)
  136. | Just (r1, r2) <- takeRegRegMoveInstr i1
  137. = if r1 == r2
  138. -- Erase any left over nop reg reg moves while we're here
  139. -- this will also catch any nop moves that the previous case
  140. -- happens to add.
  141. then cleanForward platform blockId assoc acc instrs
  142. -- If r1 has the same value as some slots and we copy r1 to r2,
  143. -- then r2 is now associated with those slots instead
  144. else do let assoc' = addAssoc (SReg r1) (SReg r2)
  145. $ delAssoc (SReg r2)
  146. $ assoc
  147. cleanForward platform blockId assoc' (li : acc) instrs
  148. cleanForward platform blockId assoc acc (li : instrs)
  149. -- Update association due to the spill.
  150. | LiveInstr (SPILL reg slot) _ <- li
  151. = let assoc' = addAssoc (SReg reg) (SSlot slot)
  152. $ delAssoc (SSlot slot)
  153. $ assoc
  154. in cleanForward platform blockId assoc' (li : acc) instrs
  155. -- Clean a reload instr.
  156. | LiveInstr (RELOAD{}) _ <- li
  157. = do (assoc', mli) <- cleanReload platform blockId assoc li
  158. case mli of
  159. Nothing -> cleanForward platform blockId assoc' acc
  160. instrs
  161. Just li' -> cleanForward platform blockId assoc' (li' : acc)
  162. instrs
  163. -- Remember the association over a jump.
  164. | LiveInstr instr _ <- li
  165. , targets <- jumpDestsOfInstr instr
  166. , not $ null targets
  167. = do mapM_ (accJumpValid assoc) targets
  168. cleanForward platform blockId assoc (li : acc) instrs
  169. -- Writing to a reg changes its value.
  170. | LiveInstr instr _ <- li
  171. , RU _ written <- regUsageOfInstr platform instr
  172. = let assoc' = foldr delAssoc assoc (map SReg $ nub written)
  173. in cleanForward platform blockId assoc' (li : acc) instrs
  174. -- | Try and rewrite a reload instruction to something more pleasing
  175. cleanReload
  176. :: Instruction instr
  177. => Platform
  178. -> BlockId
  179. -> Assoc Store
  180. -> LiveInstr instr
  181. -> CleanM (Assoc Store, Maybe (LiveInstr instr))
  182. cleanReload platform blockId assoc li@(LiveInstr (RELOAD slot reg) _)
  183. -- If the reg we're reloading already has the same value as the slot
  184. -- then we can erase the instruction outright.
  185. | elemAssoc (SSlot slot) (SReg reg) assoc
  186. = do modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
  187. return (assoc, Nothing)
  188. -- If we can find another reg with the same value as this slot then
  189. -- do a move instead of a reload.
  190. | Just reg2 <- findRegOfSlot assoc slot
  191. = do modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
  192. let assoc' = addAssoc (SReg reg) (SReg reg2)
  193. $ delAssoc (SReg reg)
  194. $ assoc
  195. return ( assoc'
  196. , Just $ LiveInstr (mkRegRegMoveInstr platform reg2 reg) Nothing)
  197. -- Gotta keep this instr.
  198. | otherwise
  199. = do -- Update the association.
  200. let assoc'
  201. = addAssoc (SReg reg) (SSlot slot)
  202. -- doing the reload makes reg and slot the same value
  203. $ delAssoc (SReg reg)
  204. -- reg value changes on reload
  205. $ assoc
  206. -- Remember that this block reloads from this slot.
  207. accBlockReloadsSlot blockId slot
  208. return (assoc', Just li)
  209. cleanReload _ _ _ _
  210. = panic "RegSpillClean.cleanReload: unhandled instr"
  211. -------------------------------------------------------------------------------
  212. -- | Clean out unneeded spill instructions,
  213. -- while walking backwards over the code.
  214. --
  215. -- If there were no reloads from a slot between a spill and the last one
  216. -- then the slot was never read and we don't need the spill.
  217. --
  218. -- SPILL r0 -> s1
  219. -- RELOAD s1 -> r2
  220. -- SPILL r3 -> s1 <--- don't need this spill
  221. -- SPILL r4 -> s1
  222. -- RELOAD s1 -> r5
  223. --
  224. -- Maintain a set of
  225. -- "slots which were spilled to but not reloaded from yet"
  226. --
  227. -- Walking backwards across the code:
  228. -- a) On a reload from a slot, remove it from the set.
  229. --
  230. -- a) On a spill from a slot
  231. -- If the slot is in set then we can erase the spill,
  232. -- because it won't be reloaded from until after the next spill.
  233. --
  234. -- otherwise
  235. -- keep the spill and add the slot to the set
  236. --
  237. -- TODO: This is mostly inter-block
  238. -- we should really be updating the noReloads set as we cross jumps also.
  239. --
  240. -- TODO: generate noReloads from liveSlotsOnEntry
  241. --
  242. cleanTopBackward
  243. :: Instruction instr
  244. => LiveCmmDecl statics instr
  245. -> CleanM (LiveCmmDecl statics instr)
  246. cleanTopBackward cmm
  247. = case cmm of
  248. CmmData{}
  249. -> return cmm
  250. CmmProc info label live sccs
  251. | LiveInfo _ _ _ liveSlotsOnEntry <- info
  252. -> do sccs' <- mapM (mapSCCM (cleanBlockBackward liveSlotsOnEntry)) sccs
  253. return $ CmmProc info label live sccs'
  254. cleanBlockBackward
  255. :: Instruction instr
  256. => BlockMap IntSet
  257. -> LiveBasicBlock instr
  258. -> CleanM (LiveBasicBlock instr)
  259. cleanBlockBackward liveSlotsOnEntry (BasicBlock blockId instrs)
  260. = do instrs_spill <- cleanBackward liveSlotsOnEntry emptyUniqSet [] instrs
  261. return $ BasicBlock blockId instrs_spill
  262. cleanBackward
  263. :: Instruction instr
  264. => BlockMap IntSet -- ^ Slots live on entry to each block
  265. -> UniqSet Int -- ^ Slots that have been spilled, but not reloaded from
  266. -> [LiveInstr instr] -- ^ acc
  267. -> [LiveInstr instr] -- ^ Instrs to clean (in forwards order)
  268. -> CleanM [LiveInstr instr] -- ^ Cleaned instrs (in backwards order)
  269. cleanBackward liveSlotsOnEntry noReloads acc lis
  270. = do reloadedBy <- gets sReloadedBy
  271. cleanBackward' liveSlotsOnEntry reloadedBy noReloads acc lis
  272. cleanBackward'
  273. :: Instruction instr
  274. => BlockMap IntSet
  275. -> UniqFM [BlockId]
  276. -> UniqSet Int
  277. -> [LiveInstr instr]
  278. -> [LiveInstr instr]
  279. -> State CleanS [LiveInstr instr]
  280. cleanBackward' _ _ _ acc []
  281. = return acc
  282. cleanBackward' liveSlotsOnEntry reloadedBy noReloads acc (li : instrs)
  283. -- If nothing ever reloads from this slot then we don't need the spill.
  284. | LiveInstr (SPILL _ slot) _ <- li
  285. , Nothing <- lookupUFM reloadedBy (SSlot slot)
  286. = do modify $ \s -> s { sCleanedSpillsAcc = sCleanedSpillsAcc s + 1 }
  287. cleanBackward liveSlotsOnEntry noReloads acc instrs
  288. | LiveInstr (SPILL _ slot) _ <- li
  289. = if elementOfUniqSet slot noReloads
  290. -- We can erase this spill because the slot won't be read until
  291. -- after the next one
  292. then do
  293. modify $ \s -> s { sCleanedSpillsAcc = sCleanedSpillsAcc s + 1 }
  294. cleanBackward liveSlotsOnEntry noReloads acc instrs
  295. else do
  296. -- This slot is being spilled to, but we haven't seen any reloads yet.
  297. let noReloads' = addOneToUniqSet noReloads slot
  298. cleanBackward liveSlotsOnEntry noReloads' (li : acc) instrs
  299. -- if we reload from a slot then it's no longer unused
  300. | LiveInstr (RELOAD slot _) _ <- li
  301. , noReloads' <- delOneFromUniqSet noReloads slot
  302. = cleanBackward liveSlotsOnEntry noReloads' (li : acc) instrs
  303. -- If a slot is live in a jump target then assume it's reloaded there.
  304. --
  305. -- TODO: A real dataflow analysis would do a better job here.
  306. -- If the target block _ever_ used the slot then we assume
  307. -- it always does, but if those reloads are cleaned the slot
  308. -- liveness map doesn't get updated.
  309. | LiveInstr instr _ <- li
  310. , targets <- jumpDestsOfInstr instr
  311. = do
  312. let slotsReloadedByTargets
  313. = IntSet.unions
  314. $ catMaybes
  315. $ map (flip lookupBlockMap liveSlotsOnEntry)
  316. $ targets
  317. let noReloads'
  318. = foldl' delOneFromUniqSet noReloads
  319. $ IntSet.toList slotsReloadedByTargets
  320. cleanBackward liveSlotsOnEntry noReloads' (li : acc) instrs
  321. -- some other instruction
  322. | otherwise
  323. = cleanBackward liveSlotsOnEntry noReloads (li : acc) instrs
  324. -- | Combine the associations from all the inward control flow edges.
  325. --
  326. collateJoinPoints :: CleanM ()
  327. collateJoinPoints
  328. = modify $ \s -> s
  329. { sJumpValid = mapUFM intersects (sJumpValidAcc s)
  330. , sJumpValidAcc = emptyUFM }
  331. intersects :: [Assoc Store] -> Assoc Store
  332. intersects [] = emptyAssoc
  333. intersects assocs = foldl1' intersectAssoc assocs
  334. -- | See if we have a reg with the same value as this slot in the association table.
  335. findRegOfSlot :: Assoc Store -> Int -> Maybe Reg
  336. findRegOfSlot assoc slot
  337. | close <- closeAssoc (SSlot slot) assoc
  338. , Just (SReg reg) <- find isStoreReg $ nonDetEltsUFM close
  339. -- See Note [Unique Determinism and code generation]
  340. = Just reg
  341. | otherwise
  342. = Nothing
  343. -------------------------------------------------------------------------------
  344. -- | Cleaner monad.
  345. type CleanM
  346. = State CleanS
  347. -- | Cleaner state.
  348. data CleanS
  349. = CleanS
  350. { -- | Regs which are valid at the start of each block.
  351. sJumpValid :: UniqFM (Assoc Store)
  352. -- | Collecting up what regs were valid across each jump.
  353. -- in the next pass we can collate these and write the results
  354. -- to sJumpValid.
  355. , sJumpValidAcc :: UniqFM [Assoc Store]
  356. -- | Map of (slot -> blocks which reload from this slot)
  357. -- used to decide if whether slot spilled to will ever be
  358. -- reloaded from on this path.
  359. , sReloadedBy :: UniqFM [BlockId]
  360. -- | Spills and reloads cleaned each pass (latest at front)
  361. , sCleanedCount :: [(Int, Int)]
  362. -- | Spills and reloads that have been cleaned in this pass so far.
  363. , sCleanedSpillsAcc :: Int
  364. , sCleanedReloadsAcc :: Int }
  365. -- | Construct the initial cleaner state.
  366. initCleanS :: CleanS
  367. initCleanS
  368. = CleanS
  369. { sJumpValid = emptyUFM
  370. , sJumpValidAcc = emptyUFM
  371. , sReloadedBy = emptyUFM
  372. , sCleanedCount = []
  373. , sCleanedSpillsAcc = 0
  374. , sCleanedReloadsAcc = 0 }
  375. -- | Remember the associations before a jump.
  376. accJumpValid :: Assoc Store -> BlockId -> CleanM ()
  377. accJumpValid assocs target
  378. = modify $ \s -> s {
  379. sJumpValidAcc = addToUFM_C (++)
  380. (sJumpValidAcc s)
  381. target
  382. [assocs] }
  383. accBlockReloadsSlot :: BlockId -> Slot -> CleanM ()
  384. accBlockReloadsSlot blockId slot
  385. = modify $ \s -> s {
  386. sReloadedBy = addToUFM_C (++)
  387. (sReloadedBy s)
  388. (SSlot slot)
  389. [blockId] }
  390. -------------------------------------------------------------------------------
  391. -- A store location can be a stack slot or a register
  392. data Store
  393. = SSlot Int
  394. | SReg Reg
  395. -- | Check if this is a reg store.
  396. isStoreReg :: Store -> Bool
  397. isStoreReg ss
  398. = case ss of
  399. SSlot _ -> False
  400. SReg _ -> True
  401. -- Spill cleaning is only done once all virtuals have been allocated to realRegs
  402. instance Uniquable Store where
  403. getUnique (SReg r)
  404. | RegReal (RealRegSingle i) <- r
  405. = mkRegSingleUnique i
  406. | RegReal (RealRegPair r1 r2) <- r
  407. = mkRegPairUnique (r1 * 65535 + r2)
  408. | otherwise
  409. = error $ "RegSpillClean.getUnique: found virtual reg during spill clean,"
  410. ++ "only real regs expected."
  411. getUnique (SSlot i) = mkRegSubUnique i -- [SLPJ] I hope "SubUnique" is ok
  412. instance Outputable Store where
  413. ppr (SSlot i) = text "slot" <> int i
  414. ppr (SReg r) = ppr r
  415. -------------------------------------------------------------------------------
  416. -- Association graphs.
  417. -- In the spill cleaner, two store locations are associated if they are known
  418. -- to hold the same value.
  419. --
  420. type Assoc a = UniqFM (UniqSet a)
  421. -- | An empty association
  422. emptyAssoc :: Assoc a
  423. emptyAssoc = emptyUFM
  424. -- | Add an association between these two things.
  425. addAssoc :: Uniquable a
  426. => a -> a -> Assoc a -> Assoc a
  427. addAssoc a b m
  428. = let m1 = addToUFM_C unionUniqSets m a (unitUniqSet b)
  429. m2 = addToUFM_C unionUniqSets m1 b (unitUniqSet a)
  430. in m2
  431. -- | Delete all associations to a node.
  432. delAssoc :: (Uniquable a)
  433. => a -> Assoc a -> Assoc a
  434. delAssoc a m
  435. | Just aSet <- lookupUFM m a
  436. , m1 <- delFromUFM m a
  437. = nonDetFoldUFM (\x m -> delAssoc1 x a m) m1 aSet
  438. -- It's OK to use nonDetFoldUFM here because deletion is commutative
  439. | otherwise = m
  440. -- | Delete a single association edge (a -> b).
  441. delAssoc1 :: Uniquable a
  442. => a -> a -> Assoc a -> Assoc a
  443. delAssoc1 a b m
  444. | Just aSet <- lookupUFM m a
  445. = addToUFM m a (delOneFromUniqSet aSet b)
  446. | otherwise = m
  447. -- | Check if these two things are associated.
  448. elemAssoc :: (Uniquable a)
  449. => a -> a -> Assoc a -> Bool
  450. elemAssoc a b m
  451. = elementOfUniqSet b (closeAssoc a m)
  452. -- | Find the refl. trans. closure of the association from this point.
  453. closeAssoc :: (Uniquable a)
  454. => a -> Assoc a -> UniqSet a
  455. closeAssoc a assoc
  456. = closeAssoc' assoc emptyUniqSet (unitUniqSet a)
  457. where
  458. closeAssoc' assoc visited toVisit
  459. = case nonDetEltsUFM toVisit of
  460. -- See Note [Unique Determinism and code generation]
  461. -- nothing else to visit, we're done
  462. [] -> visited
  463. (x:_)
  464. -- we've already seen this node
  465. | elementOfUniqSet x visited
  466. -> closeAssoc' assoc visited (delOneFromUniqSet toVisit x)
  467. -- haven't seen this node before,
  468. -- remember to visit all its neighbors
  469. | otherwise
  470. -> let neighbors
  471. = case lookupUFM assoc x of
  472. Nothing -> emptyUniqSet
  473. Just set -> set
  474. in closeAssoc' assoc
  475. (addOneToUniqSet visited x)
  476. (unionUniqSets toVisit neighbors)
  477. -- | Intersect two associations.
  478. intersectAssoc :: Assoc a -> Assoc a -> Assoc a
  479. intersectAssoc a b
  480. = intersectUFM_C (intersectUniqSets) a b