root/compiler/cmm/CmmSpillReload.hs

Revision 5b167f5edad7d3268de20452da7af05c38972f7c, 7.8 KB (checked in by Simon Marlow <marlowsd@…>, 9 months ago)

Snapshot of codegen refactoring to share with simonpj

  • Property mode set to 100644
Line 
1{-# LANGUAGE GADTs, NoMonoLocalBinds, FlexibleContexts #-}
2-- Norman likes local bindings
3-- If this module lives on I'd like to get rid of this flag in due course
4
5{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
6
7-- TODO: Get rid of this flag:
8{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}
9
10module CmmSpillReload
11  ( dualLivenessWithInsertion
12  )
13where
14
15import BlockId
16import Cmm
17import CmmUtils
18import CmmLive
19import OptimizationFuel
20
21import Control.Monad
22import Outputable hiding (empty)
23import qualified Outputable as PP
24import UniqSet
25
26import Compiler.Hoopl hiding (Unique)
27import Data.Maybe
28import Prelude hiding (succ, zip)
29
30{- Note [Overview of spill/reload]
31~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32The point of this module is to insert spills and reloads to establish
33the invariant that at a call or any proc point with an established
34protocol all live variables not expected in registers are sitting on the
35stack.  We use a backward dual liveness analysis (both traditional
36register liveness as well as register slot liveness on the stack) to
37insert spills and reloads.  It should be followed by a forward
38transformation to sink reloads as deeply as possible, so as to reduce
39register pressure: this transformation is performed by
40CmmRewriteAssignments.
41
42A variable can be expected to be live in a register, live on the
43stack, or both.  This analysis ensures that spills and reloads are
44inserted as needed to make sure that every live variable needed
45after a call is available on the stack.  Spills are placed immediately
46after their reaching definitions, but reloads are placed immediately
47after a return from a call (the entry point.)
48
49Note that we offer no guarantees about the consistency of the value
50in memory and the value in the register, except that they are
51equal across calls/procpoints.  If the variable is changed, this
52mapping breaks: but as the original value of the register may still
53be useful in a different context, the memory location is not updated.
54-}
55
56data DualLive = DualLive { on_stack :: RegSet, in_regs :: RegSet }
57
58changeStack, changeRegs :: (RegSet -> RegSet) -> DualLive -> DualLive
59changeStack f live = live { on_stack = f (on_stack live) }
60changeRegs  f live = live { in_regs  = f (in_regs  live) }
61
62dualLiveLattice :: DataflowLattice DualLive
63dualLiveLattice = DataflowLattice "variables live in registers and on stack" empty add
64    where empty = DualLive emptyRegSet emptyRegSet
65          add _ (OldFact old) (NewFact new) = (changeIf $ change1 || change2, DualLive stack regs)
66            where (change1, stack) = add1 (on_stack old) (on_stack new)
67                  (change2, regs)  = add1 (in_regs old)  (in_regs new)
68          add1 old new = if sizeUniqSet join > sizeUniqSet old then (True, join) else (False, old)
69            where join = unionUniqSets old new
70
71dualLivenessWithInsertion :: BlockSet -> CmmGraph -> FuelUniqSM CmmGraph
72dualLivenessWithInsertion procPoints g =
73  liftM fst $ dataflowPassBwd g [] $ analRewBwd dualLiveLattice
74                                                (dualLiveTransfers (g_entry g) procPoints)
75                                                (insertSpillsAndReloads g procPoints)
76
77-- Note [Live registers on entry to procpoints]
78-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
79-- Remember that the transfer function is only ever run on the rewritten
80-- version of a graph, and the rewrite function for spills and reloads
81-- enforces the invariant that no local registers are live on entry to
82-- a procpoint.  Accordingly, we check for this invariant here.  An old
83-- version of this code incorrectly claimed that any live registers were
84-- live on the stack before entering the function: this is wrong, but
85-- didn't cause bugs because it never actually was invoked.
86
87dualLiveTransfers :: BlockId -> BlockSet -> (BwdTransfer CmmNode DualLive)
88dualLiveTransfers entry procPoints = mkBTransfer3 first middle last
89    where first :: CmmNode C O -> DualLive -> DualLive
90          first (CmmEntry id) live -- See Note [Live registers on entry to procpoints]
91            | id == entry || setMember id procPoints = noLiveOnEntry id (in_regs live) live
92            | otherwise                              = live
93
94          middle :: CmmNode O O -> DualLive -> DualLive
95          middle m = changeStack updSlots
96                   . changeRegs  updRegs
97            where -- Reuse middle of liveness analysis from CmmLive
98                  updRegs = case getBTransfer3 xferLive of (_, middle, _) -> middle m
99
100                  updSlots live = foldSlotsUsed reload (foldSlotsDefd spill live m) m
101                  spill  live s@(RegSlot r, _, _) = check s $ deleteFromRegSet live r
102                  spill  live _ = live
103                  reload live s@(RegSlot r, _, _) = check s $ extendRegSet live r
104                  reload live _ = live
105                  -- Ensure the assignment refers to the entirety of the
106                  -- register slot (and not just a slice).
107                  check (RegSlot (LocalReg _ ty), o, w) x
108                     | o == w && w == widthInBytes (typeWidth ty) = x
109                  check _ _ = panic "dualLiveTransfers: slices unsupported"
110
111          -- Register analysis is identical to liveness analysis from CmmLive.
112          last :: CmmNode O C -> FactBase DualLive -> DualLive
113          last l fb = changeRegs (gen_kill l) $ case l of
114            CmmCall {cml_cont=Nothing} -> empty
115            CmmCall {cml_cont=Just k}  -> keep_stack_only k
116            CmmForeignCall {succ=k}    -> keep_stack_only k
117            _                          -> joinOutFacts dualLiveLattice l fb
118            where empty = fact_bot dualLiveLattice
119                  lkp k = fromMaybe empty (lookupFact k fb)
120                  keep_stack_only k = DualLive (on_stack (lkp k)) emptyRegSet
121
122insertSpillsAndReloads :: CmmGraph -> BlockSet -> CmmBwdRewrite DualLive
123insertSpillsAndReloads graph procPoints = deepBwdRw3 first middle nothing
124  -- Beware: deepBwdRw with one polymorphic function seems more reasonable here,
125  -- but GHC miscompiles it, see bug #4044.
126    where first :: CmmNode C O -> Fact O DualLive -> CmmReplGraph C O
127          first e@(CmmEntry id) live = return $
128            if id /= (g_entry graph) && setMember id procPoints then
129              case map reload (uniqSetToList (in_regs live)) of
130                [] -> Nothing
131                is -> Just $ mkFirst e <*> mkMiddles is
132            else Nothing
133          -- EZY: There was some dead code for handling the case where
134          -- we were not splitting procedures.  Check Git history if
135          -- you're interested (circa e26ea0f41).
136
137          middle :: CmmNode O O -> Fact O DualLive -> CmmReplGraph O O
138          -- Don't add spills next to reloads.
139          middle (CmmAssign (CmmLocal reg) (CmmLoad (CmmStackSlot (RegSlot reg') _) _)) _ | reg == reg' = return Nothing
140          -- Spill if register is live on stack.
141          middle m@(CmmAssign (CmmLocal reg) _) live
142            | reg `elemRegSet` on_stack live = return (Just (mkMiddles [m, spill reg]))
143          middle _ _ = return Nothing
144
145          nothing _ _ = return Nothing
146
147spill, reload :: LocalReg -> CmmNode O O
148spill  r = CmmStore  (regSlot r) (CmmReg $ CmmLocal r)
149reload r = CmmAssign (CmmLocal r) (CmmLoad (regSlot r) $ localRegType r)
150
151---------------------
152-- prettyprinting
153
154ppr_regs :: String -> RegSet -> SDoc
155ppr_regs s regs = text s <+> commafy (map ppr $ uniqSetToList regs)
156  where commafy xs = hsep $ punctuate comma xs
157
158instance Outputable DualLive where
159  ppr (DualLive {in_regs = regs, on_stack = stack}) =
160      if isEmptyUniqSet regs && isEmptyUniqSet stack then
161          text "<nothing-live>"
162      else
163          nest 2 $ fsep [if isEmptyUniqSet regs then PP.empty
164                         else (ppr_regs "live in regs =" regs),
165                         if isEmptyUniqSet stack then PP.empty
166                         else (ppr_regs "live on stack =" stack)]
Note: See TracBrowser for help on using the browser.