1 -- | Clean out unneeded spill/reload instrs
3 -- * Handling of join points
7 -- RELOAD SLOT(0), %r1 RELOAD SLOT(0), %r1
12 -- RELOAD SLOT(0), %r1
16 -- So long as %r1 hasn't been written to in A, B or C then we don't need the
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_)
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.
26 module RegSpillClean (
46 -- | Clean out unneeded spill/reloads from this top level thing.
47 cleanSpills :: LiveCmmTop -> LiveCmmTop
49 = evalState (cleanSpin 0 cmm) initCleanS
51 -- | do one pass of cleaning
52 cleanSpin :: Int -> LiveCmmTop -> CleanM LiveCmmTop
55 cleanSpin spinCount code
56 = do jumpValid <- gets sJumpValid
63 $ cleanSpin' spinCount code
66 cleanSpin spinCount code
68 -- init count of cleaned spills/reloads
70 { sCleanedSpillsAcc = 0
71 , sCleanedReloadsAcc = 0 }
73 code' <- mapBlockTopM cleanBlock code
75 -- During the cleaning of each block we collected information about what regs
76 -- were valid across each jump. Based on this, work out whether it will be
77 -- safe to erase reloads after join points for the next pass.
80 -- remember how many spills/reloads we cleaned in this pass
81 spills <- gets sCleanedSpillsAcc
82 reloads <- gets sCleanedReloadsAcc
84 { sCleanedCount = (spills, reloads) : sCleanedCount s }
86 -- if nothing was cleaned in this pass or the last one
87 -- then we're done and it's time to bail out
88 cleanedCount <- gets sCleanedCount
89 if take 2 cleanedCount == [(0, 0), (0, 0)]
92 -- otherwise go around again
93 else cleanSpin (spinCount + 1) code'
96 -- | Clean one basic block
97 cleanBlock :: LiveBasicBlock -> CleanM LiveBasicBlock
98 cleanBlock (BasicBlock id instrs)
99 = do jumpValid <- gets sJumpValid
100 let assoc = case lookupUFM jumpValid id of
102 Nothing -> emptyAssoc
104 instrs_reload <- cleanFwd assoc [] instrs
105 instrs_spill <- cleanSpill emptyUniqSet [] instrs_reload
106 return $ BasicBlock id instrs_spill
109 -- | Clean out unneeded reload instructions.
110 -- Walking forwards across the code
111 -- On a reload, if we know a reg already has the same value as a slot
112 -- then we don't need to do the reload.
115 :: Assoc Store -- ^ two store locations are associated if they have the same value
116 -> [LiveInstr] -- ^ acc
117 -> [LiveInstr] -- ^ instrs to clean (in backwards order)
118 -> CleanM [LiveInstr] -- ^ cleaned instrs (in forward order)
123 -- write out live range joins via spill slots to just a spill and a reg-reg move
124 -- hopefully the spill will be also be cleaned in the next pass
126 cleanFwd assoc acc (Instr i1 live1 : Instr i2 _ : instrs)
128 | SPILL reg1 slot1 <- i1
129 , RELOAD slot2 reg2 <- i2
132 modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
134 (Instr i1 live1 : Instr (mkRegRegMoveInstr reg1 reg2) Nothing : instrs)
137 cleanFwd assoc acc (li@(Instr i1 _) : instrs)
138 | Just (r1, r2) <- isRegRegMove i1
140 -- erase any left over nop reg reg moves while we're here
141 -- this will also catch any nop moves that the "write out live range joins" case above
143 then cleanFwd assoc acc instrs
145 -- if r1 has the same value as some slots and we copy r1 to r2,
146 -- then r2 is now associated with those slots instead
147 else do let assoc' = addAssoc (SReg r1) (SReg r2)
151 cleanFwd assoc' (li : acc) instrs
154 cleanFwd assoc acc (li@(Instr instr _) : instrs)
156 | SPILL reg slot <- instr
157 = let assoc' = addAssoc (SReg reg) (SSlot slot) -- doing the spill makes reg and slot the same value
158 $ delAssoc (SSlot slot) -- slot value changes on spill
160 in cleanFwd assoc' (li : acc) instrs
162 -- clean a reload instr
164 = do (assoc', mli) <- cleanReload assoc li
166 Nothing -> cleanFwd assoc' acc instrs
167 Just li' -> cleanFwd assoc' (li' : acc) instrs
169 -- remember the association over a jump
170 | targets <- jumpDests instr []
172 = do mapM_ (accJumpValid assoc) targets
173 cleanFwd assoc (li : acc) instrs
175 -- writing to a reg changes its value.
176 | RU _ written <- regUsage instr
177 = let assoc' = foldr delAssoc assoc (map SReg $ nub written)
178 in cleanFwd assoc' (li : acc) instrs
181 -- | Try and rewrite a reload instruction to something more pleasing
183 cleanReload :: Assoc Store -> LiveInstr -> CleanM (Assoc Store, Maybe LiveInstr)
184 cleanReload assoc li@(Instr (RELOAD slot reg) _)
186 -- if the reg we're reloading already has the same value as the slot
187 -- then we can erase the instruction outright
188 | elemAssoc (SSlot slot) (SReg reg) assoc
189 = do modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
190 return (assoc, Nothing)
192 -- if we can find another reg with the same value as this slot then
193 -- do a move instead of a reload.
194 | Just reg2 <- findRegOfSlot assoc slot
195 = do modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
197 let assoc' = addAssoc (SReg reg) (SReg reg2)
198 $ delAssoc (SReg reg)
201 return (assoc', Just $ Instr (mkRegRegMoveInstr reg2 reg) Nothing)
203 -- gotta keep this instr
204 -- update the association
206 = do let assoc' = addAssoc (SReg reg) (SSlot slot) -- doing the reload makes reg and slot the same value
207 $ delAssoc (SReg reg) -- reg value changes on reload
210 return (assoc', Just li)
213 = panic "RegSpillClean.cleanReload: unhandled instr"
216 -- | Clean out unneeded spill instructions.
217 -- Walking backwards across the code.
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.
222 :: UniqSet Int -- ^ slots that have been spilled, but not reloaded from
223 -> [LiveInstr] -- ^ acc
224 -> [LiveInstr] -- ^ instrs to clean (in forwards order)
225 -> CleanM [LiveInstr] -- ^ cleaned instrs (in backwards order)
230 cleanSpill unused acc (li@(Instr instr _) : instrs)
231 | SPILL _ slot <- instr
232 = if elementOfUniqSet slot unused
234 -- we can erase this spill because the slot won't be read until after the next one
236 modify $ \s -> s { sCleanedSpillsAcc = sCleanedSpillsAcc s + 1 }
237 cleanSpill unused acc instrs
240 -- slots start off unused
241 let unused' = addOneToUniqSet unused slot
242 cleanSpill unused' (li : acc) instrs
244 -- if we reload from a slot then it's no longer unused
245 | RELOAD slot _ <- instr
246 , unused' <- delOneFromUniqSet unused slot
247 = cleanSpill unused' (li : acc) instrs
249 -- some other instruction
251 = cleanSpill unused (li : acc) instrs
254 -- collateJoinPoints:
256 -- | combine the associations from all the inward control flow edges.
258 collateJoinPoints :: CleanM ()
261 { sJumpValid = mapUFM intersects (sJumpValidAcc s)
262 , sJumpValidAcc = emptyUFM }
264 intersects :: [Assoc Store] -> Assoc Store
265 intersects [] = emptyAssoc
266 intersects assocs = foldl1' intersectAssoc assocs
269 -- | See if we have a reg with the same value as this slot in the association table.
270 findRegOfSlot :: Assoc Store -> Int -> Maybe Reg
271 findRegOfSlot assoc slot
272 | close <- closeAssoc (SSlot slot) assoc
273 , Just (SReg reg) <- find isStoreReg $ uniqSetToList close
281 type CleanM = State CleanS
284 { -- regs which are valid at the start of each block.
285 sJumpValid :: UniqFM (Assoc Store)
287 -- collecting up what regs were valid across each jump.
288 -- in the next pass we can collate these and write the results
290 , sJumpValidAcc :: UniqFM [Assoc Store]
292 -- spills/reloads cleaned each pass (latest at front)
293 , sCleanedCount :: [(Int, Int)]
295 -- spills/reloads that have been cleaned in this pass so far.
296 , sCleanedSpillsAcc :: Int
297 , sCleanedReloadsAcc :: Int }
302 { sJumpValid = emptyUFM
303 , sJumpValidAcc = emptyUFM
307 , sCleanedSpillsAcc = 0
308 , sCleanedReloadsAcc = 0 }
311 -- | Remember the associations before a jump
312 accJumpValid :: Assoc Store -> BlockId -> CleanM ()
313 accJumpValid assocs target
315 sJumpValidAcc = addToUFM_C (++)
321 -- A store location can be a stack slot or a register
327 -- | Check if this is a reg store
328 isStoreReg :: Store -> Bool
334 -- spill cleaning is only done once all virtuals have been allocated to realRegs
336 instance Uniquable Store where
342 = error "RegSpillClean.getUnique: found virtual reg during spill clean, only real regs expected."
344 getUnique (SSlot i) = mkUnique 'S' i
346 instance Outputable Store where
347 ppr (SSlot i) = text "slot" <> int i
352 -- Association graphs.
353 -- In the spill cleaner, two store locations are associated if they are known
354 -- to hold the same value.
356 type Assoc a = UniqFM (UniqSet a)
358 -- | an empty association
359 emptyAssoc :: Assoc a
360 emptyAssoc = emptyUFM
363 -- | add an association between these two things
364 addAssoc :: Uniquable a
365 => a -> a -> Assoc a -> Assoc a
368 = let m1 = addToUFM_C unionUniqSets m a (unitUniqSet b)
369 m2 = addToUFM_C unionUniqSets m1 b (unitUniqSet a)
373 -- | delete all associations to a node
374 delAssoc :: (Outputable a, Uniquable a)
375 => a -> Assoc a -> Assoc a
378 | Just aSet <- lookupUFM m a
379 , m1 <- delFromUFM m a
380 = foldUniqSet (\x m -> delAssoc1 x a m) m1 aSet
385 -- | delete a single association edge (a -> b)
386 delAssoc1 :: Uniquable a
387 => a -> a -> Assoc a -> Assoc a
390 | Just aSet <- lookupUFM m a
391 = addToUFM m a (delOneFromUniqSet aSet b)
396 -- | check if these two things are associated
397 elemAssoc :: (Outputable a, Uniquable a)
398 => a -> a -> Assoc a -> Bool
401 = elementOfUniqSet b (closeAssoc a m)
403 -- | find the refl. trans. closure of the association from this point
404 closeAssoc :: (Outputable a, Uniquable a)
405 => a -> Assoc a -> UniqSet a
408 = closeAssoc' assoc emptyUniqSet (unitUniqSet a)
410 closeAssoc' assoc visited toVisit
411 = case uniqSetToList toVisit of
413 -- nothing else to visit, we're done
418 -- we've already seen this node
419 | elementOfUniqSet x visited
420 -> closeAssoc' assoc visited (delOneFromUniqSet toVisit x)
422 -- haven't seen this node before,
423 -- remember to visit all its neighbors
426 = case lookupUFM assoc x of
427 Nothing -> emptyUniqSet
431 (addOneToUniqSet visited x)
432 (unionUniqSets toVisit neighbors)
437 => Assoc a -> Assoc a -> Assoc a
440 = intersectUFM_C (intersectUniqSets) a b