PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

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

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