NCG: Move the graph allocator into its own dir
[ghc-hetmet.git] / compiler / nativeGen / RegSpillClean.hs
diff --git a/compiler/nativeGen/RegSpillClean.hs b/compiler/nativeGen/RegSpillClean.hs
deleted file mode 100644 (file)
index 2ecd450..0000000
+++ /dev/null
@@ -1,515 +0,0 @@
-{-# OPTIONS -fno-warn-missing-signatures #-}
--- | Clean out unneeded spill\/reload instrs
---
--- * Handling of join points
---
---   B1:                          B2:
---    ...                          ...
---       RELOAD SLOT(0), %r1          RELOAD SLOT(0), %r1
---       ... A ...                    ... B ...
---       jump B3                      jump B3
---
---                B3: ... C ...
---                    RELOAD SLOT(0), %r1
---                    ...
---
--- the plan:
---     So long as %r1 hasn't been written to in A, B or C then we don't need the
---     reload in B3.
---
---     What we really care about here is that on the entry to B3, %r1 will always
---     have the same value that is in SLOT(0) (ie, %r1 is _valid_)
---
---     This also works if the reloads in B1\/B2 were spills instead, because
---     spilling %r1 to a slot makes that slot have the same value as %r1.
---
-
-module RegSpillClean (
-       cleanSpills
-)
-where
-
-import BlockId
-import RegLiveness
-import RegAllocInfo
-import MachRegs
-import MachInstrs
-import Cmm
-
-import UniqSet
-import UniqFM
-import Unique
-import State
-import Outputable
-import Util
-
-import Data.Maybe
-import Data.List        ( find, nub )
-
---
-type Slot = Int
-
-
--- | Clean out unneeded spill\/reloads from this top level thing.
-cleanSpills :: LiveCmmTop -> LiveCmmTop
-cleanSpills cmm
-       = evalState (cleanSpin 0 cmm) initCleanS
-
--- | do one pass of cleaning
-cleanSpin :: Int -> LiveCmmTop -> CleanM LiveCmmTop
-
-{-
-cleanSpin spinCount code
- = do  jumpValid       <- gets sJumpValid
-       pprTrace "cleanSpin"
-               (  int spinCount
-               $$ text "--- code"
-               $$ ppr code
-               $$ text "--- joins"
-               $$ ppr jumpValid)
-        $ cleanSpin' spinCount code
--}
-
-cleanSpin spinCount code
- = do
-       -- init count of cleaned spills\/reloads
-       modify $ \s -> s
-               { sCleanedSpillsAcc     = 0
-               , sCleanedReloadsAcc    = 0
-               , sReloadedBy           = emptyUFM }
-
-       code_forward    <- mapBlockTopM cleanBlockForward  code
-       code_backward   <- mapBlockTopM cleanBlockBackward code_forward
-
-       -- During the cleaning of each block we collected information about what regs
-       --      were valid across each jump. Based on this, work out whether it will be
-       --      safe to erase reloads after join points for the next pass.
-       collateJoinPoints
-
-       -- remember how many spills\/reloads we cleaned in this pass
-       spills          <- gets sCleanedSpillsAcc
-       reloads         <- gets sCleanedReloadsAcc
-       modify $ \s -> s
-               { sCleanedCount = (spills, reloads) : sCleanedCount s }
-
-       -- if nothing was cleaned in this pass or the last one
-       --      then we're done and it's time to bail out
-       cleanedCount    <- gets sCleanedCount
-       if take 2 cleanedCount == [(0, 0), (0, 0)]
-          then return code
-
-       -- otherwise go around again
-          else cleanSpin (spinCount + 1) code_backward
-
-
--- | Clean one basic block
-cleanBlockForward :: LiveBasicBlock -> CleanM LiveBasicBlock
-cleanBlockForward (BasicBlock blockId instrs)
- = do
-       -- see if we have a valid association for the entry to this block
-       jumpValid       <- gets sJumpValid
-       let assoc       = case lookupUFM jumpValid blockId of
-                               Just assoc      -> assoc
-                               Nothing         -> emptyAssoc
-
-       instrs_reload   <- cleanForward    blockId assoc [] instrs
-       return  $ BasicBlock blockId instrs_reload
-
-
-cleanBlockBackward :: LiveBasicBlock -> CleanM LiveBasicBlock
-cleanBlockBackward (BasicBlock blockId instrs)
- = do  instrs_spill    <- cleanBackward  emptyUniqSet  [] instrs
-       return  $ BasicBlock blockId instrs_spill
-
-
-
-
--- | Clean out unneeded reload instructions.
---     Walking forwards across the code
---       On a reload, if we know a reg already has the same value as a slot
---       then we don't need to do the reload.
---
-cleanForward
-       :: BlockId              -- ^ the block that we're currently in
-       -> Assoc Store          -- ^ two store locations are associated if they have the same value
-       -> [LiveInstr]          -- ^ acc
-       -> [LiveInstr]          -- ^ instrs to clean (in backwards order)
-       -> CleanM [LiveInstr]   -- ^ cleaned instrs  (in forward   order)
-
-cleanForward _ _ acc []
-       = return acc
-
--- write out live range joins via spill slots to just a spill and a reg-reg move
---     hopefully the spill will be also be cleaned in the next pass
---
-cleanForward blockId assoc acc (Instr i1 live1 : Instr i2 _ : instrs)
-
-       | SPILL  reg1  slot1    <- i1
-       , RELOAD slot2 reg2     <- i2
-       , slot1 == slot2
-       = do
-               modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
-               cleanForward blockId assoc acc
-                       (Instr i1 live1 : Instr (mkRegRegMoveInstr reg1 reg2) Nothing : instrs)
-
-
-cleanForward blockId assoc acc (li@(Instr i1 _) : instrs)
-       | Just (r1, r2) <- isRegRegMove i1
-       = if r1 == r2
-               -- erase any left over nop reg reg moves while we're here
-               --      this will also catch any nop moves that the "write out live range joins" case above
-               --      happens to add
-               then cleanForward blockId assoc acc instrs
-
-               -- if r1 has the same value as some slots and we copy r1 to r2,
-               --      then r2 is now associated with those slots instead
-               else do let assoc'      = addAssoc (SReg r1) (SReg r2)
-                                       $ delAssoc (SReg r2)
-                                       $ assoc
-
-                       cleanForward blockId assoc' (li : acc) instrs
-
-
-cleanForward blockId assoc acc (li@(Instr instr _) : instrs)
-
-       -- update association due to the spill
-       | SPILL reg slot        <- instr
-       = let   assoc'  = addAssoc (SReg reg)  (SSlot slot)
-                       $ delAssoc (SSlot slot)
-                       $ assoc
-         in    cleanForward blockId assoc' (li : acc) instrs
-
-       -- clean a reload instr
-       | RELOAD{}              <- instr
-       = do    (assoc', mli)   <- cleanReload blockId assoc li
-               case mli of
-                Nothing        -> cleanForward blockId assoc' acc              instrs
-                Just li'       -> cleanForward blockId assoc' (li' : acc)      instrs
-
-       -- remember the association over a jump
-       | targets       <- jumpDests instr []
-       , not $ null targets
-       = do    mapM_ (accJumpValid assoc) targets
-               cleanForward blockId assoc (li : acc) instrs
-
-       -- writing to a reg changes its value.
-       | RU _ written  <- regUsage instr
-       = let assoc'    = foldr delAssoc assoc (map SReg $ nub written)
-         in  cleanForward blockId assoc' (li : acc) instrs
-
-
--- | Try and rewrite a reload instruction to something more pleasing
---
-cleanReload :: BlockId -> Assoc Store -> LiveInstr -> CleanM (Assoc Store, Maybe LiveInstr)
-cleanReload blockId assoc li@(Instr (RELOAD slot reg) _)
-
-       -- if the reg we're reloading already has the same value as the slot
-       --      then we can erase the instruction outright
-       | elemAssoc (SSlot slot) (SReg reg) assoc
-       = do    modify  $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
-               return  (assoc, Nothing)
-
-       -- if we can find another reg with the same value as this slot then
-       --      do a move instead of a reload.
-       | Just reg2     <- findRegOfSlot assoc slot
-       = do    modify $ \s -> s { sCleanedReloadsAcc = sCleanedReloadsAcc s + 1 }
-
-               let assoc'      = addAssoc (SReg reg) (SReg reg2)
-                               $ delAssoc (SReg reg)
-                               $ assoc
-
-               return  (assoc', Just $ Instr (mkRegRegMoveInstr reg2 reg) Nothing)
-
-       -- gotta keep this instr
-       | otherwise
-       = do    -- update the association
-               let assoc'      = addAssoc (SReg reg)  (SSlot slot)     -- doing the reload makes reg and slot the same value
-                               $ delAssoc (SReg reg)                   -- reg value changes on reload
-                               $ assoc
-
-               -- remember that this block reloads from this slot
-               accBlockReloadsSlot blockId slot
-
-               return  (assoc', Just li)
-
-cleanReload _ _ _
-       = panic "RegSpillClean.cleanReload: unhandled instr"
-
-
--- | Clean out unneeded spill instructions.
---
---      If there were no reloads from a slot between a spill and the last one
---      then the slot was never read and we don't need the spill.
---
---     SPILL   r0 -> s1
---     RELOAD  s1 -> r2
---     SPILL   r3 -> s1        <--- don't need this spill
---     SPILL   r4 -> s1
---     RELOAD  s1 -> r5
---
---     Maintain a set of
---             "slots which were spilled to but not reloaded from yet"
---
---     Walking backwards across the code:
---      a) On a reload from a slot, remove it from the set.
---
---      a) On a spill from a slot
---             If the slot is in set then we can erase the spill,
---                     because it won't be reloaded from until after the next spill.
---
---             otherwise
---                     keep the spill and add the slot to the set
---
--- TODO: This is mostly inter-block
---      we should really be updating the noReloads set as we cross jumps also.
---
-cleanBackward
-       :: UniqSet Int          -- ^ slots that have been spilled, but not reloaded from
-       -> [LiveInstr]          -- ^ acc
-       -> [LiveInstr]          -- ^ instrs to clean (in forwards order)
-       -> CleanM [LiveInstr]   -- ^ cleaned instrs  (in backwards order)
-
-
-cleanBackward noReloads acc lis
- = do  reloadedBy      <- gets sReloadedBy
-       cleanBackward' reloadedBy noReloads acc lis
-
-cleanBackward' _ _      acc []
-       = return  acc
-
-cleanBackward' reloadedBy noReloads acc (li@(Instr instr _) : instrs)
-
-       -- if nothing ever reloads from this slot then we don't need the spill
-       | SPILL _ slot  <- instr
-       , Nothing       <- lookupUFM reloadedBy (SSlot slot)
-       = do    modify $ \s -> s { sCleanedSpillsAcc = sCleanedSpillsAcc s + 1 }
-               cleanBackward noReloads acc instrs
-
-       | SPILL _ slot  <- instr
-       = if elementOfUniqSet slot noReloads
-
-          -- we can erase this spill because the slot won't be read until after the next one
-          then do
-               modify $ \s -> s { sCleanedSpillsAcc = sCleanedSpillsAcc s + 1 }
-               cleanBackward noReloads acc instrs
-
-          else do
-               -- this slot is being spilled to, but we haven't seen any reloads yet.
-               let noReloads'  = addOneToUniqSet noReloads slot
-               cleanBackward noReloads' (li : acc) instrs
-
-       -- if we reload from a slot then it's no longer unused
-       | RELOAD slot _         <- instr
-       , noReloads'            <- delOneFromUniqSet noReloads slot
-       = cleanBackward noReloads' (li : acc) instrs
-
-       -- some other instruction
-       | otherwise
-       = cleanBackward noReloads (li : acc) instrs
-
-
--- collateJoinPoints:
---
--- | combine the associations from all the inward control flow edges.
---
-collateJoinPoints :: CleanM ()
-collateJoinPoints
- = modify $ \s -> s
-       { sJumpValid    = mapUFM intersects (sJumpValidAcc s)
-       , sJumpValidAcc = emptyUFM }
-
-intersects :: [Assoc Store]    -> Assoc Store
-intersects []          = emptyAssoc
-intersects assocs      = foldl1' intersectAssoc assocs
-
-
--- | See if we have a reg with the same value as this slot in the association table.
-findRegOfSlot :: Assoc Store -> Int -> Maybe Reg
-findRegOfSlot assoc slot
-       | close                 <- closeAssoc (SSlot slot) assoc
-       , Just (SReg reg)       <- find isStoreReg $ uniqSetToList close
-       = Just reg
-
-       | otherwise
-       = Nothing
-
-
----------------
-type CleanM = State CleanS
-data CleanS
-       = CleanS
-       { -- regs which are valid at the start of each block.
-         sJumpValid            :: UniqFM (Assoc Store)
-
-         -- collecting up what regs were valid across each jump.
-         --    in the next pass we can collate these and write the results
-         --    to sJumpValid.
-       , sJumpValidAcc         :: UniqFM [Assoc Store]
-
-         -- map of (slot -> blocks which reload from this slot)
-         --    used to decide if whether slot spilled to will ever be
-         --    reloaded from on this path.
-       , sReloadedBy           :: UniqFM [BlockId]
-
-         -- spills\/reloads cleaned each pass (latest at front)
-       , sCleanedCount         :: [(Int, Int)]
-
-         -- spills\/reloads that have been cleaned in this pass so far.
-       , sCleanedSpillsAcc     :: Int
-       , sCleanedReloadsAcc    :: Int }
-
-initCleanS :: CleanS
-initCleanS
-       = CleanS
-       { sJumpValid            = emptyUFM
-       , sJumpValidAcc         = emptyUFM
-
-       , sReloadedBy           = emptyUFM
-
-       , sCleanedCount         = []
-
-       , sCleanedSpillsAcc     = 0
-       , sCleanedReloadsAcc    = 0 }
-
-
--- | Remember the associations before a jump
-accJumpValid :: Assoc Store -> BlockId -> CleanM ()
-accJumpValid assocs target
- = modify $ \s -> s {
-       sJumpValidAcc = addToUFM_C (++)
-                               (sJumpValidAcc s)
-                               target
-                               [assocs] }
-
-
-accBlockReloadsSlot :: BlockId -> Slot -> CleanM ()
-accBlockReloadsSlot blockId slot
- = modify $ \s -> s {
-       sReloadedBy = addToUFM_C (++)
-                               (sReloadedBy s)
-                               (SSlot slot)
-                               [blockId] }
-
-
---------------
--- A store location can be a stack slot or a register
---
-data Store
-       = SSlot Int
-       | SReg  Reg
-
--- | Check if this is a reg store
-isStoreReg :: Store -> Bool
-isStoreReg ss
- = case ss of
-       SSlot _ -> False
-       SReg  _ -> True
-
--- spill cleaning is only done once all virtuals have been allocated to realRegs
---
-instance Uniquable Store where
-    getUnique (SReg  r)
-       | RealReg i     <- r
-       = mkUnique 'R' i
-
-       | otherwise
-       = error "RegSpillClean.getUnique: found virtual reg during spill clean, only real regs expected."
-
-    getUnique (SSlot i)                        = mkUnique 'S' i
-
-instance Outputable Store where
-       ppr (SSlot i)   = text "slot" <> int i
-       ppr (SReg  r)   = ppr r
-
-
---------------
--- Association graphs.
---     In the spill cleaner, two store locations are associated if they are known
---     to hold the same value.
---
-type Assoc a   = UniqFM (UniqSet a)
-
--- | an empty association
-emptyAssoc :: Assoc a
-emptyAssoc     = emptyUFM
-
-
--- | add an association between these two things
-addAssoc :: Uniquable a
-        => a -> a -> Assoc a -> Assoc a
-
-addAssoc a b m
- = let m1      = addToUFM_C unionUniqSets m  a (unitUniqSet b)
-       m2      = addToUFM_C unionUniqSets m1 b (unitUniqSet a)
-   in  m2
-
-
--- | delete all associations to a node
-delAssoc :: (Outputable a, Uniquable a)
-        => a -> Assoc a -> Assoc a
-
-delAssoc a m
-       | Just aSet     <- lookupUFM  m a
-       , m1            <- delFromUFM m a
-       = foldUniqSet (\x m -> delAssoc1 x a m) m1 aSet
-
-       | otherwise     = m
-
-
--- | delete a single association edge (a -> b)
-delAssoc1 :: Uniquable a
-       => a -> a -> Assoc a -> Assoc a
-
-delAssoc1 a b m
-       | Just aSet     <- lookupUFM m a
-       = addToUFM m a (delOneFromUniqSet aSet b)
-
-       | otherwise     = m
-
-
--- | check if these two things are associated
-elemAssoc :: (Outputable a, Uniquable a)
-         => a -> a -> Assoc a -> Bool
-
-elemAssoc a b m
-       = elementOfUniqSet b (closeAssoc a m)
-
--- | find the refl. trans. closure of the association from this point
-closeAssoc :: (Outputable a, Uniquable a)
-       => a -> Assoc a -> UniqSet a
-
-closeAssoc a assoc
- =     closeAssoc' assoc emptyUniqSet (unitUniqSet a)
- where
-       closeAssoc' assoc visited toVisit
-        = case uniqSetToList toVisit of
-
-               -- nothing else to visit, we're done
-               []      -> visited
-
-               (x:_)
-
-                -- we've already seen this node
-                |  elementOfUniqSet x visited
-                -> closeAssoc' assoc visited (delOneFromUniqSet toVisit x)
-
-                -- haven't seen this node before,
-                --     remember to visit all its neighbors
-                |  otherwise
-                -> let neighbors
-                        = case lookupUFM assoc x of
-                               Nothing         -> emptyUniqSet
-                               Just set        -> set
-
-                  in closeAssoc' assoc
-                       (addOneToUniqSet visited x)
-                       (unionUniqSets   toVisit neighbors)
-
--- | intersect
-intersectAssoc
-       :: Uniquable a
-       => Assoc a -> Assoc a -> Assoc a
-
-intersectAssoc a b
-       = intersectUFM_C (intersectUniqSets) a b
-