1 {-# OPTIONS -fno-warn-missing-signatures #-}
2 -- | Clean out unneeded spill/reload instrs
4 -- * Handling of join points
8 -- RELOAD SLOT(0), %r1 RELOAD SLOT(0), %r1
13 -- RELOAD SLOT(0), %r1
17 -- So long as %r1 hasn't been written to in A, B or C then we don't need the
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_)
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.
27 module RegSpillClean (
46 import Data.List ( find, nub )
52 -- | Clean out unneeded spill/reloads from this top level thing.
53 cleanSpills :: LiveCmmTop -> LiveCmmTop
55 = evalState (cleanSpin 0 cmm) initCleanS
57 -- | do one pass of cleaning
58 cleanSpin :: Int -> LiveCmmTop -> CleanM LiveCmmTop
61 cleanSpin spinCount code
62 = do jumpValid <- gets sJumpValid
69 $ cleanSpin' spinCount code
72 cleanSpin spinCount code
74 -- init count of cleaned spills/reloads
76 { sCleanedSpillsAcc = 0
77 , sCleanedReloadsAcc = 0
78 , sReloadedBy = emptyUFM }
80 code_forward <- mapBlockTopM cleanBlockForward code
81 code_backward <- mapBlockTopM cleanBlockBackward code_forward
83 -- During the cleaning of each block we collected information about what regs
84 -- were valid across each jump. Based on this, work out whether it will be
85 -- safe to erase reloads after join points for the next pass.
88 -- remember how many spills/reloads we cleaned in this pass
89 spills <- gets sCleanedSpillsAcc
90 reloads <- gets sCleanedReloadsAcc
92 { sCleanedCount = (spills, reloads) : sCleanedCount s }
94 -- if nothing was cleaned in this pass or the last one
95 -- then we're done and it's time to bail out
96 cleanedCount <- gets sCleanedCount
97 if take 2 cleanedCount == [(0, 0), (0, 0)]
100 -- otherwise go around again
101 else cleanSpin (spinCount + 1) code_backward
104 -- | Clean one basic block
105 cleanBlockForward :: LiveBasicBlock -> CleanM LiveBasicBlock
106 cleanBlockForward (BasicBlock blockId instrs)
108 -- see if we have a valid association for the entry to this block
109 jumpValid <- gets sJumpValid
110 let assoc = case lookupUFM jumpValid blockId of
112 Nothing -> emptyAssoc
114 instrs_reload <- cleanForward blockId assoc [] instrs
115 return $ BasicBlock blockId instrs_reload
118 cleanBlockBackward :: LiveBasicBlock -> CleanM LiveBasicBlock
119 cleanBlockBackward (BasicBlock blockId instrs)
120 = do instrs_spill <- cleanBackward emptyUniqSet [] instrs
121 return $ BasicBlock blockId instrs_spill
126 -- | Clean out unneeded reload instructions.
127 -- Walking forwards across the code
128 -- On a reload, if we know a reg already has the same value as a slot
129 -- then we don't need to do the reload.
132 :: BlockId -- ^ the block that we're currently in
133 -> Assoc Store -- ^ two store locations are associated if they have the same value
134 -> [LiveInstr] -- ^ acc
135 -> [LiveInstr] -- ^ instrs to clean (in backwards order)
136 -> CleanM [LiveInstr] -- ^ cleaned instrs (in forward order)
138 cleanForward _ _ acc []
141 -- write out live range joins via spill slots to just a spill and a reg-reg move
142 -- hopefully the spill will be also be cleaned in the next pass
144 cleanForward blockId assoc acc (Instr i1 live1 : Instr i2 _ : instrs)
146 | SPILL reg1 slot1 <- i1
147 , RELOAD slot2 reg2 <- i2
150 modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
151 cleanForward blockId assoc acc
152 (Instr i1 live1 : Instr (mkRegRegMoveInstr reg1 reg2) Nothing : instrs)
155 cleanForward blockId assoc acc (li@(Instr i1 _) : instrs)
156 | Just (r1, r2) <- isRegRegMove i1
158 -- erase any left over nop reg reg moves while we're here
159 -- this will also catch any nop moves that the "write out live range joins" case above
161 then cleanForward blockId assoc acc instrs
163 -- if r1 has the same value as some slots and we copy r1 to r2,
164 -- then r2 is now associated with those slots instead
165 else do let assoc' = addAssoc (SReg r1) (SReg r2)
169 cleanForward blockId assoc' (li : acc) instrs
172 cleanForward blockId assoc acc (li@(Instr instr _) : instrs)
174 -- update association due to the spill
175 | SPILL reg slot <- instr
176 = let assoc' = addAssoc (SReg reg) (SSlot slot)
177 $ delAssoc (SSlot slot)
179 in cleanForward blockId assoc' (li : acc) instrs
181 -- clean a reload instr
183 = do (assoc', mli) <- cleanReload blockId assoc li
185 Nothing -> cleanForward blockId assoc' acc instrs
186 Just li' -> cleanForward blockId assoc' (li' : acc) instrs
188 -- remember the association over a jump
189 | targets <- jumpDests instr []
191 = do mapM_ (accJumpValid assoc) targets
192 cleanForward blockId assoc (li : acc) instrs
194 -- writing to a reg changes its value.
195 | RU _ written <- regUsage instr
196 = let assoc' = foldr delAssoc assoc (map SReg $ nub written)
197 in cleanForward blockId assoc' (li : acc) instrs
200 -- | Try and rewrite a reload instruction to something more pleasing
202 cleanReload :: BlockId -> Assoc Store -> LiveInstr -> CleanM (Assoc Store, Maybe LiveInstr)
203 cleanReload blockId assoc li@(Instr (RELOAD slot reg) _)
205 -- if the reg we're reloading already has the same value as the slot
206 -- then we can erase the instruction outright
207 | elemAssoc (SSlot slot) (SReg reg) assoc
208 = do modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
209 return (assoc, Nothing)
211 -- if we can find another reg with the same value as this slot then
212 -- do a move instead of a reload.
213 | Just reg2 <- findRegOfSlot assoc slot
214 = do modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
216 let assoc' = addAssoc (SReg reg) (SReg reg2)
217 $ delAssoc (SReg reg)
220 return (assoc', Just $ Instr (mkRegRegMoveInstr reg2 reg) Nothing)
222 -- gotta keep this instr
224 = do -- update the association
225 let assoc' = addAssoc (SReg reg) (SSlot slot) -- doing the reload makes reg and slot the same value
226 $ delAssoc (SReg reg) -- reg value changes on reload
229 -- remember that this block reloads from this slot
230 accBlockReloadsSlot blockId slot
232 return (assoc', Just li)
235 = panic "RegSpillClean.cleanReload: unhandled instr"
238 -- | Clean out unneeded spill instructions.
240 -- If there were no reloads from a slot between a spill and the last one
241 -- then the slot was never read and we don't need the spill.
245 -- SPILL r3 -> s1 <--- don't need this spill
250 -- "slots which were spilled to but not reloaded from yet"
252 -- Walking backwards across the code:
253 -- a) On a reload from a slot, remove it from the set.
255 -- a) On a spill from a slot
256 -- If the slot is in set then we can erase the spill,
257 -- because it won't be reloaded from until after the next spill.
260 -- keep the spill and add the slot to the set
262 -- TODO: This is mostly inter-block
263 -- we should really be updating the noReloads set as we cross jumps also.
266 :: UniqSet Int -- ^ slots that have been spilled, but not reloaded from
267 -> [LiveInstr] -- ^ acc
268 -> [LiveInstr] -- ^ instrs to clean (in forwards order)
269 -> CleanM [LiveInstr] -- ^ cleaned instrs (in backwards order)
272 cleanBackward noReloads acc lis
273 = do reloadedBy <- gets sReloadedBy
274 cleanBackward' reloadedBy noReloads acc lis
276 cleanBackward' _ _ acc []
279 cleanBackward' reloadedBy noReloads acc (li@(Instr instr _) : instrs)
281 -- if nothing ever reloads from this slot then we don't need the spill
282 | SPILL _ slot <- instr
283 , Nothing <- lookupUFM reloadedBy (SSlot slot)
284 = do modify $ \s -> s { sCleanedSpillsAcc = sCleanedSpillsAcc s + 1 }
285 cleanBackward noReloads acc instrs
287 | SPILL _ slot <- instr
288 = if elementOfUniqSet slot noReloads
290 -- we can erase this spill because the slot won't be read until after the next one
292 modify $ \s -> s { sCleanedSpillsAcc = sCleanedSpillsAcc s + 1 }
293 cleanBackward noReloads acc instrs
296 -- this slot is being spilled to, but we haven't seen any reloads yet.
297 let noReloads' = addOneToUniqSet noReloads slot
298 cleanBackward noReloads' (li : acc) instrs
300 -- if we reload from a slot then it's no longer unused
301 | RELOAD slot _ <- instr
302 , noReloads' <- delOneFromUniqSet noReloads slot
303 = cleanBackward noReloads' (li : acc) instrs
305 -- some other instruction
307 = cleanBackward noReloads (li : acc) instrs
310 -- collateJoinPoints:
312 -- | combine the associations from all the inward control flow edges.
314 collateJoinPoints :: CleanM ()
317 { sJumpValid = mapUFM intersects (sJumpValidAcc s)
318 , sJumpValidAcc = emptyUFM }
320 intersects :: [Assoc Store] -> Assoc Store
321 intersects [] = emptyAssoc
322 intersects assocs = foldl1' intersectAssoc assocs
325 -- | See if we have a reg with the same value as this slot in the association table.
326 findRegOfSlot :: Assoc Store -> Int -> Maybe Reg
327 findRegOfSlot assoc slot
328 | close <- closeAssoc (SSlot slot) assoc
329 , Just (SReg reg) <- find isStoreReg $ uniqSetToList close
337 type CleanM = State CleanS
340 { -- regs which are valid at the start of each block.
341 sJumpValid :: UniqFM (Assoc Store)
343 -- collecting up what regs were valid across each jump.
344 -- in the next pass we can collate these and write the results
346 , sJumpValidAcc :: UniqFM [Assoc Store]
348 -- map of (slot -> blocks which reload from this slot)
349 -- used to decide if whether slot spilled to will ever be
350 -- reloaded from on this path.
351 , sReloadedBy :: UniqFM [BlockId]
353 -- spills/reloads cleaned each pass (latest at front)
354 , sCleanedCount :: [(Int, Int)]
356 -- spills/reloads that have been cleaned in this pass so far.
357 , sCleanedSpillsAcc :: Int
358 , sCleanedReloadsAcc :: Int }
363 { sJumpValid = emptyUFM
364 , sJumpValidAcc = emptyUFM
366 , sReloadedBy = emptyUFM
370 , sCleanedSpillsAcc = 0
371 , sCleanedReloadsAcc = 0 }
374 -- | Remember the associations before a jump
375 accJumpValid :: Assoc Store -> BlockId -> CleanM ()
376 accJumpValid assocs target
378 sJumpValidAcc = addToUFM_C (++)
384 accBlockReloadsSlot :: BlockId -> Slot -> CleanM ()
385 accBlockReloadsSlot blockId slot
387 sReloadedBy = addToUFM_C (++)
394 -- A store location can be a stack slot or a register
400 -- | Check if this is a reg store
401 isStoreReg :: Store -> Bool
407 -- spill cleaning is only done once all virtuals have been allocated to realRegs
409 instance Uniquable Store where
415 = error "RegSpillClean.getUnique: found virtual reg during spill clean, only real regs expected."
417 getUnique (SSlot i) = mkUnique 'S' i
419 instance Outputable Store where
420 ppr (SSlot i) = text "slot" <> int i
425 -- Association graphs.
426 -- In the spill cleaner, two store locations are associated if they are known
427 -- to hold the same value.
429 type Assoc a = UniqFM (UniqSet a)
431 -- | an empty association
432 emptyAssoc :: Assoc a
433 emptyAssoc = emptyUFM
436 -- | add an association between these two things
437 addAssoc :: Uniquable a
438 => a -> a -> Assoc a -> Assoc a
441 = let m1 = addToUFM_C unionUniqSets m a (unitUniqSet b)
442 m2 = addToUFM_C unionUniqSets m1 b (unitUniqSet a)
446 -- | delete all associations to a node
447 delAssoc :: (Outputable a, Uniquable a)
448 => a -> Assoc a -> Assoc a
451 | Just aSet <- lookupUFM m a
452 , m1 <- delFromUFM m a
453 = foldUniqSet (\x m -> delAssoc1 x a m) m1 aSet
458 -- | delete a single association edge (a -> b)
459 delAssoc1 :: Uniquable a
460 => a -> a -> Assoc a -> Assoc a
463 | Just aSet <- lookupUFM m a
464 = addToUFM m a (delOneFromUniqSet aSet b)
469 -- | check if these two things are associated
470 elemAssoc :: (Outputable a, Uniquable a)
471 => a -> a -> Assoc a -> Bool
474 = elementOfUniqSet b (closeAssoc a m)
476 -- | find the refl. trans. closure of the association from this point
477 closeAssoc :: (Outputable a, Uniquable a)
478 => a -> Assoc a -> UniqSet a
481 = closeAssoc' assoc emptyUniqSet (unitUniqSet a)
483 closeAssoc' assoc visited toVisit
484 = case uniqSetToList toVisit of
486 -- nothing else to visit, we're done
491 -- we've already seen this node
492 | elementOfUniqSet x visited
493 -> closeAssoc' assoc visited (delOneFromUniqSet toVisit x)
495 -- haven't seen this node before,
496 -- remember to visit all its neighbors
499 = case lookupUFM assoc x of
500 Nothing -> emptyUniqSet
504 (addOneToUniqSet visited x)
505 (unionUniqSets toVisit neighbors)
510 => Assoc a -> Assoc a -> Assoc a
513 = intersectUFM_C (intersectUniqSets) a b