| 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 | |
|---|
| 10 | module CmmSpillReload |
|---|
| 11 | ( dualLivenessWithInsertion |
|---|
| 12 | ) |
|---|
| 13 | where |
|---|
| 14 | |
|---|
| 15 | import BlockId |
|---|
| 16 | import Cmm |
|---|
| 17 | import CmmUtils |
|---|
| 18 | import CmmLive |
|---|
| 19 | import OptimizationFuel |
|---|
| 20 | |
|---|
| 21 | import Control.Monad |
|---|
| 22 | import Outputable hiding (empty) |
|---|
| 23 | import qualified Outputable as PP |
|---|
| 24 | import UniqSet |
|---|
| 25 | |
|---|
| 26 | import Compiler.Hoopl hiding (Unique) |
|---|
| 27 | import Data.Maybe |
|---|
| 28 | import Prelude hiding (succ, zip) |
|---|
| 29 | |
|---|
| 30 | {- Note [Overview of spill/reload] |
|---|
| 31 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 32 | The point of this module is to insert spills and reloads to establish |
|---|
| 33 | the invariant that at a call or any proc point with an established |
|---|
| 34 | protocol all live variables not expected in registers are sitting on the |
|---|
| 35 | stack. We use a backward dual liveness analysis (both traditional |
|---|
| 36 | register liveness as well as register slot liveness on the stack) to |
|---|
| 37 | insert spills and reloads. It should be followed by a forward |
|---|
| 38 | transformation to sink reloads as deeply as possible, so as to reduce |
|---|
| 39 | register pressure: this transformation is performed by |
|---|
| 40 | CmmRewriteAssignments. |
|---|
| 41 | |
|---|
| 42 | A variable can be expected to be live in a register, live on the |
|---|
| 43 | stack, or both. This analysis ensures that spills and reloads are |
|---|
| 44 | inserted as needed to make sure that every live variable needed |
|---|
| 45 | after a call is available on the stack. Spills are placed immediately |
|---|
| 46 | after their reaching definitions, but reloads are placed immediately |
|---|
| 47 | after a return from a call (the entry point.) |
|---|
| 48 | |
|---|
| 49 | Note that we offer no guarantees about the consistency of the value |
|---|
| 50 | in memory and the value in the register, except that they are |
|---|
| 51 | equal across calls/procpoints. If the variable is changed, this |
|---|
| 52 | mapping breaks: but as the original value of the register may still |
|---|
| 53 | be useful in a different context, the memory location is not updated. |
|---|
| 54 | -} |
|---|
| 55 | |
|---|
| 56 | data DualLive = DualLive { on_stack :: RegSet, in_regs :: RegSet } |
|---|
| 57 | |
|---|
| 58 | changeStack, changeRegs :: (RegSet -> RegSet) -> DualLive -> DualLive |
|---|
| 59 | changeStack f live = live { on_stack = f (on_stack live) } |
|---|
| 60 | changeRegs f live = live { in_regs = f (in_regs live) } |
|---|
| 61 | |
|---|
| 62 | dualLiveLattice :: DataflowLattice DualLive |
|---|
| 63 | dualLiveLattice = 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 | |
|---|
| 71 | dualLivenessWithInsertion :: BlockSet -> CmmGraph -> FuelUniqSM CmmGraph |
|---|
| 72 | dualLivenessWithInsertion 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 | |
|---|
| 87 | dualLiveTransfers :: BlockId -> BlockSet -> (BwdTransfer CmmNode DualLive) |
|---|
| 88 | dualLiveTransfers 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 | |
|---|
| 122 | insertSpillsAndReloads :: CmmGraph -> BlockSet -> CmmBwdRewrite DualLive |
|---|
| 123 | insertSpillsAndReloads 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 | |
|---|
| 147 | spill, reload :: LocalReg -> CmmNode O O |
|---|
| 148 | spill r = CmmStore (regSlot r) (CmmReg $ CmmLocal r) |
|---|
| 149 | reload r = CmmAssign (CmmLocal r) (CmmLoad (regSlot r) $ localRegType r) |
|---|
| 150 | |
|---|
| 151 | --------------------- |
|---|
| 152 | -- prettyprinting |
|---|
| 153 | |
|---|
| 154 | ppr_regs :: String -> RegSet -> SDoc |
|---|
| 155 | ppr_regs s regs = text s <+> commafy (map ppr $ uniqSetToList regs) |
|---|
| 156 | where commafy xs = hsep $ punctuate comma xs |
|---|
| 157 | |
|---|
| 158 | instance 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)] |
|---|