PageRenderTime 57ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

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

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