PageRenderTime 46ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

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

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