PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/ghc-7.0.4/compiler/nativeGen/RegAlloc/Graph/SpillClean.hs

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