| 1 | {-# LANGUAGE RankNTypes, ScopedTypeVariables, GADTs, EmptyDataDecls, PatternGuards, TypeFamilies, MultiParamTypeClasses #-} |
|---|
| 2 | {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-} -- bug in GHC |
|---|
| 3 | |
|---|
| 4 | {- Notes about the genesis of Hoopl7 |
|---|
| 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 6 | Hoopl7 has the following major chages |
|---|
| 7 | |
|---|
| 8 | a) GMany has symmetric entry and exit |
|---|
| 9 | b) GMany closed-entry does not record a BlockId |
|---|
| 10 | c) GMany open-exit does not record a BlockId |
|---|
| 11 | d) The body of a GMany is called Body |
|---|
| 12 | e) A Body is just a list of blocks, not a map. I've argued |
|---|
| 13 | elsewhere that this is consistent with (c) |
|---|
| 14 | |
|---|
| 15 | A consequence is that Graph is no longer an instance of Edges, |
|---|
| 16 | but nevertheless I managed to keep the ARF and ARB signatures |
|---|
| 17 | nice and uniform. |
|---|
| 18 | |
|---|
| 19 | This was made possible by |
|---|
| 20 | |
|---|
| 21 | * FwdTransfer looks like this: |
|---|
| 22 | type FwdTransfer n f |
|---|
| 23 | = forall e x. n e x -> Fact e f -> Fact x f |
|---|
| 24 | type family Fact x f :: * |
|---|
| 25 | type instance Fact C f = FactBase f |
|---|
| 26 | type instance Fact O f = f |
|---|
| 27 | |
|---|
| 28 | Note that the incoming fact is a Fact (not just 'f' as in Hoopl5,6). |
|---|
| 29 | It's up to the *transfer function* to look up the appropriate fact |
|---|
| 30 | in the FactBase for a closed-entry node. Example: |
|---|
| 31 | constProp (Label l) fb = lookupFact fb l |
|---|
| 32 | That is how Hoopl can avoid having to know the block-id for the |
|---|
| 33 | first node: it defers to the client. |
|---|
| 34 | |
|---|
| 35 | [Side note: that means the client must know about |
|---|
| 36 | bottom, in case the looupFact returns Nothing] |
|---|
| 37 | |
|---|
| 38 | * Note also that FwdTransfer *returns* a Fact too; |
|---|
| 39 | that is, the types in both directions are symmetrical. |
|---|
| 40 | Previously we returned a [(BlockId,f)] but I could not see |
|---|
| 41 | how to make everything line up if we do this. |
|---|
| 42 | |
|---|
| 43 | Indeed, the main shortcoming of Hoopl7 is that we are more |
|---|
| 44 | or less forced into this uniform representation of the facts |
|---|
| 45 | flowing into or out of a closed node/block/graph, whereas |
|---|
| 46 | previously we had more flexibility. |
|---|
| 47 | |
|---|
| 48 | In exchange the code is neater, with fewer distinct types. |
|---|
| 49 | And morally a FactBase is equivalent to [(BlockId,f)] and |
|---|
| 50 | nearly equivalent to (BlockId -> f). |
|---|
| 51 | |
|---|
| 52 | * I've realised that forwardBlockList and backwardBlockList |
|---|
| 53 | both need (Edges n), and that goes everywhere. |
|---|
| 54 | |
|---|
| 55 | * I renamed BlockId to Label |
|---|
| 56 | -} |
|---|
| 57 | |
|---|
| 58 | module Compiler.Hoopl.DataflowNest |
|---|
| 59 | ( DataflowLattice(..), JoinFun, OldFact(..), NewFact(..), Fact |
|---|
| 60 | , ChangeFlag(..), changeIf |
|---|
| 61 | , FwdPass(..), FwdTransfer, mkFTransfer, mkFTransfer', getFTransfers |
|---|
| 62 | , FwdRes(..), FwdRewrite, mkFRewrite, mkFRewrite', getFRewrites |
|---|
| 63 | , BwdPass(..), BwdTransfer, mkBTransfer, mkBTransfer', getBTransfers |
|---|
| 64 | , BwdRes(..), BwdRewrite, mkBRewrite, mkBRewrite', getBRewrites |
|---|
| 65 | , analyzeAndRewriteFwd, analyzeAndRewriteBwd |
|---|
| 66 | , analyzeAndRewriteFwd', analyzeAndRewriteBwd' |
|---|
| 67 | ) |
|---|
| 68 | where |
|---|
| 69 | |
|---|
| 70 | import Data.Maybe |
|---|
| 71 | |
|---|
| 72 | import Compiler.Hoopl.Fuel |
|---|
| 73 | import Compiler.Hoopl.Graph |
|---|
| 74 | import Compiler.Hoopl.MkGraph |
|---|
| 75 | import qualified Compiler.Hoopl.GraphUtil as U |
|---|
| 76 | import Compiler.Hoopl.Label |
|---|
| 77 | import Compiler.Hoopl.Util |
|---|
| 78 | |
|---|
| 79 | ----------------------------------------------------------------------------- |
|---|
| 80 | -- DataflowLattice |
|---|
| 81 | ----------------------------------------------------------------------------- |
|---|
| 82 | |
|---|
| 83 | data DataflowLattice a = DataflowLattice |
|---|
| 84 | { fact_name :: String -- Documentation |
|---|
| 85 | , fact_bot :: a -- Lattice bottom element |
|---|
| 86 | , fact_extend :: JoinFun a -- Lattice join plus change flag |
|---|
| 87 | -- (changes iff result > old fact) |
|---|
| 88 | , fact_do_logging :: Bool -- log changes |
|---|
| 89 | } |
|---|
| 90 | -- ^ A transfer function might want to use the logging flag |
|---|
| 91 | -- to control debugging, as in for example, it updates just one element |
|---|
| 92 | -- in a big finite map. We don't want Hoopl to show the whole fact, |
|---|
| 93 | -- and only the transfer function knows exactly what changed. |
|---|
| 94 | |
|---|
| 95 | type JoinFun a = Label -> OldFact a -> NewFact a -> (ChangeFlag, a) |
|---|
| 96 | -- the label argument is for debugging purposes only |
|---|
| 97 | newtype OldFact a = OldFact a |
|---|
| 98 | newtype NewFact a = NewFact a |
|---|
| 99 | |
|---|
| 100 | data ChangeFlag = NoChange | SomeChange deriving (Eq, Ord) |
|---|
| 101 | changeIf :: Bool -> ChangeFlag |
|---|
| 102 | changeIf changed = if changed then SomeChange else NoChange |
|---|
| 103 | |
|---|
| 104 | |
|---|
| 105 | ----------------------------------------------------------------------------- |
|---|
| 106 | -- Analyze and rewrite forward: the interface |
|---|
| 107 | ----------------------------------------------------------------------------- |
|---|
| 108 | |
|---|
| 109 | data FwdPass n f |
|---|
| 110 | = FwdPass { fp_lattice :: DataflowLattice f |
|---|
| 111 | , fp_transfer :: FwdTransfer n f |
|---|
| 112 | , fp_rewrite :: FwdRewrite n f } |
|---|
| 113 | |
|---|
| 114 | newtype FwdTransfer n f |
|---|
| 115 | = FwdTransfers { getFTransfers :: |
|---|
| 116 | ( n C O -> f -> f |
|---|
| 117 | , n O O -> f -> f |
|---|
| 118 | , n O C -> f -> FactBase f |
|---|
| 119 | ) } |
|---|
| 120 | |
|---|
| 121 | newtype FwdRewrite n f |
|---|
| 122 | = FwdRewrites { getFRewrites :: |
|---|
| 123 | ( n C O -> f -> Maybe (FwdRes n f C O) |
|---|
| 124 | , n O O -> f -> Maybe (FwdRes n f O O) |
|---|
| 125 | , n O C -> f -> Maybe (FwdRes n f O C) |
|---|
| 126 | ) } |
|---|
| 127 | data FwdRes n f e x = FwdRes (AGraph n e x) (FwdRewrite n f) |
|---|
| 128 | -- result of a rewrite is a new graph and a (possibly) new rewrite function |
|---|
| 129 | |
|---|
| 130 | mkFTransfer :: (n C O -> f -> f) |
|---|
| 131 | -> (n O O -> f -> f) |
|---|
| 132 | -> (n O C -> f -> FactBase f) |
|---|
| 133 | -> FwdTransfer n f |
|---|
| 134 | mkFTransfer f m l = FwdTransfers (f, m, l) |
|---|
| 135 | |
|---|
| 136 | mkFTransfer' :: (forall e x . n e x -> f -> Fact x f) -> FwdTransfer n f |
|---|
| 137 | mkFTransfer' f = FwdTransfers (f, f, f) |
|---|
| 138 | |
|---|
| 139 | mkFRewrite :: (n C O -> f -> Maybe (FwdRes n f C O)) |
|---|
| 140 | -> (n O O -> f -> Maybe (FwdRes n f O O)) |
|---|
| 141 | -> (n O C -> f -> Maybe (FwdRes n f O C)) |
|---|
| 142 | -> FwdRewrite n f |
|---|
| 143 | mkFRewrite f m l = FwdRewrites (f, m, l) |
|---|
| 144 | |
|---|
| 145 | mkFRewrite' :: (forall e x . n e x -> f -> Maybe (FwdRes n f e x)) -> FwdRewrite n f |
|---|
| 146 | mkFRewrite' f = FwdRewrites (f, f, f) |
|---|
| 147 | |
|---|
| 148 | |
|---|
| 149 | type family Fact x f :: * |
|---|
| 150 | type instance Fact C f = FactBase f |
|---|
| 151 | type instance Fact O f = f |
|---|
| 152 | |
|---|
| 153 | analyzeAndRewriteFwd |
|---|
| 154 | :: forall n f. Edges n |
|---|
| 155 | => FwdPass n f |
|---|
| 156 | -> Body n -> FactBase f |
|---|
| 157 | -> FuelMonad (Body n, FactBase f) |
|---|
| 158 | |
|---|
| 159 | analyzeAndRewriteFwd pass body facts = analyzeAndRewriteFwd' pass g facts >>= finish |
|---|
| 160 | where |
|---|
| 161 | finish :: (Graph n C C, FactBase f, MaybeO C f) -> FuelMonad (Body n, FactBase f) |
|---|
| 162 | finish (GMany NothingO body NothingO, fb, NothingO) = return (body, fb) |
|---|
| 163 | g = GMany NothingO body NothingO |
|---|
| 164 | |
|---|
| 165 | -- | if the graph being analyzed is open at the entry, there must |
|---|
| 166 | -- be no other entry point, or all goes horribly wrong... |
|---|
| 167 | analyzeAndRewriteFwd' |
|---|
| 168 | :: forall n f e x. Edges n |
|---|
| 169 | => FwdPass n f |
|---|
| 170 | -> Graph n e x -> Fact e f |
|---|
| 171 | -> FuelMonad (Graph n e x, FactBase f, MaybeO x f) |
|---|
| 172 | analyzeAndRewriteFwd' pass g f = |
|---|
| 173 | do (rg, fout) <- arfGraph pass g f |
|---|
| 174 | let (g', fb) = normalizeGraph rg |
|---|
| 175 | return (g', fb, distinguishedExitFact g' fout) |
|---|
| 176 | |
|---|
| 177 | distinguishedExitFact :: forall n e x f . Graph n e x -> Fact x f -> MaybeO x f |
|---|
| 178 | distinguishedExitFact g f = maybe g |
|---|
| 179 | where maybe :: Graph n e x -> MaybeO x f |
|---|
| 180 | maybe GNil = JustO f |
|---|
| 181 | maybe (GUnit {}) = JustO f |
|---|
| 182 | maybe (GMany _ _ x) = case x of NothingO -> NothingO |
|---|
| 183 | JustO _ -> JustO f |
|---|
| 184 | |
|---|
| 185 | ---------------------------------------------------------------- |
|---|
| 186 | -- Forward Implementation |
|---|
| 187 | ---------------------------------------------------------------- |
|---|
| 188 | |
|---|
| 189 | type ARF' n f thing e x |
|---|
| 190 | = thing e x -> f -> FuelMonad (RG f n e x, Fact x f) |
|---|
| 191 | -- ^ Analyze and rewrite forward |
|---|
| 192 | |
|---|
| 193 | type ARFX' n f thing e x |
|---|
| 194 | = thing e x -> Fact e f -> FuelMonad (RG f n e x, Fact x f) |
|---|
| 195 | -- ^ Analyze and rewrite forward extended -- can take @FactBase f@ |
|---|
| 196 | |
|---|
| 197 | type ARFt n f thing = forall e x . ARF' n f thing e x |
|---|
| 198 | type ARFXt n f thing = forall e x . ARFX' n f thing e x |
|---|
| 199 | |
|---|
| 200 | arfGraph :: forall n f . Edges n => FwdPass n f -> ARFXt n f (Graph n) |
|---|
| 201 | arfGraph pass = graph |
|---|
| 202 | where {- nested type synonyms would be so lovely here |
|---|
| 203 | type ARF thing = forall e x . ARF' n f thing e x |
|---|
| 204 | type ARFX thing = forall e x . ARFX' n f thing e x |
|---|
| 205 | -} |
|---|
| 206 | graph :: ARFXt n f (Graph n) |
|---|
| 207 | block :: ARFt n f (Block n) |
|---|
| 208 | node :: forall e x . (ShapeLifter e x) => ARF' n f n e x |
|---|
| 209 | arfBody :: Body n -> FactBase f -> FuelMonad (RG f n C C, FactBase f) |
|---|
| 210 | -- Outgoing factbase is restricted to Labels *not* in |
|---|
| 211 | -- in the Body; the facts for Labels *in* |
|---|
| 212 | -- the Body are in the 'RG f n C C' |
|---|
| 213 | cat :: (info -> FuelMonad (RG f n e a, info')) |
|---|
| 214 | -> (info' -> FuelMonad (RG f n a x, info'')) |
|---|
| 215 | -> (info -> FuelMonad (RG f n e x, info'')) |
|---|
| 216 | |
|---|
| 217 | graph GNil = \f -> return (rgnil, f) |
|---|
| 218 | graph (GUnit blk) = block blk |
|---|
| 219 | graph (GMany NothingO body NothingO) = arfBody body |
|---|
| 220 | graph (GMany NothingO body (JustO exit)) = arfBody body `cat` arfx block exit |
|---|
| 221 | graph (GMany (JustO entry) body NothingO) = block entry `cat` arfBody body |
|---|
| 222 | graph (GMany (JustO entry) body (JustO exit)) |
|---|
| 223 | = (block entry `cat` arfBody body) `cat` arfx block exit |
|---|
| 224 | |
|---|
| 225 | -- type demonstration |
|---|
| 226 | _block :: forall e x . ARF' n f (Block n) e x |
|---|
| 227 | _block = block |
|---|
| 228 | |
|---|
| 229 | -- Lift from nodes to blocks |
|---|
| 230 | block (BFirst n) = node n |
|---|
| 231 | block (BMiddle n) = node n |
|---|
| 232 | block (BLast n) = node n |
|---|
| 233 | block (BCat b1 b2) = block b1 `cat` block b2 |
|---|
| 234 | block (BHead h n) = block h `cat` node n |
|---|
| 235 | block (BTail n t) = node n `cat` block t |
|---|
| 236 | block (BClosed h t)= block h `cat` block t |
|---|
| 237 | |
|---|
| 238 | node thenode f |
|---|
| 239 | = do { mb_g <- withFuel (frewrite pass thenode f) |
|---|
| 240 | ; case mb_g of |
|---|
| 241 | Nothing -> return (rgunit f (unit thenode), |
|---|
| 242 | ftransfer pass thenode f) |
|---|
| 243 | Just (FwdRes ag rw) -> do { g <- graphOfAGraph ag |
|---|
| 244 | ; let pass' = pass { fp_rewrite = rw } |
|---|
| 245 | ; arfGraph pass' g (elift thenode f) } } |
|---|
| 246 | |
|---|
| 247 | -- | Compose fact transformers and concatenate the resulting |
|---|
| 248 | -- rewritten graphs. |
|---|
| 249 | {-# INLINE cat #-} |
|---|
| 250 | cat ft1 ft2 f = do { (g1,f1) <- ft1 f |
|---|
| 251 | ; (g2,f2) <- ft2 f1 |
|---|
| 252 | ; return (g1 `rgCat` g2, f2) } |
|---|
| 253 | |
|---|
| 254 | arfx :: forall thing x . |
|---|
| 255 | Edges thing => ARF' n f thing C x -> ARFX' n f thing C x |
|---|
| 256 | arfx arf thing fb = |
|---|
| 257 | arf thing $ fromJust $ lookupFact (joinInFacts lattice fb) $ entryLabel thing |
|---|
| 258 | where lattice = fp_lattice pass |
|---|
| 259 | -- joinInFacts adds debugging information |
|---|
| 260 | |
|---|
| 261 | |
|---|
| 262 | -- Outgoing factbase is restricted to Labels *not* in |
|---|
| 263 | -- in the Body; the facts for Labels *in* |
|---|
| 264 | -- the Body are in the 'RG f n C C' |
|---|
| 265 | arfBody blocks init_fbase |
|---|
| 266 | = fixpoint True (fp_lattice pass) do_block init_fbase $ |
|---|
| 267 | forwardBlockList (factBaseLabels init_fbase) blocks |
|---|
| 268 | where |
|---|
| 269 | do_block b f = do (g, fb) <- block b $ lookupF pass (entryLabel b) f |
|---|
| 270 | return (g, factBaseList fb) |
|---|
| 271 | |
|---|
| 272 | |
|---|
| 273 | |
|---|
| 274 | -- Join all the incoming facts with bottom. |
|---|
| 275 | -- We know the results _shouldn't change_, but the transfer |
|---|
| 276 | -- functions might, for example, generate some debugging traces. |
|---|
| 277 | joinInFacts :: DataflowLattice f -> FactBase f -> FactBase f |
|---|
| 278 | joinInFacts (DataflowLattice {fact_bot = bot, fact_extend = fe}) fb = |
|---|
| 279 | mkFactBase $ map botJoin $ factBaseList fb |
|---|
| 280 | where botJoin (l, f) = (l, snd $ fe l (OldFact bot) (NewFact f)) |
|---|
| 281 | |
|---|
| 282 | forwardBlockList :: (Edges n, LabelsPtr entry) |
|---|
| 283 | => entry -> Body n -> [Block n C C] |
|---|
| 284 | -- This produces a list of blocks in order suitable for forward analysis, |
|---|
| 285 | -- along with the list of Labels it may depend on for facts. |
|---|
| 286 | forwardBlockList entries blks = postorder_dfs_from (bodyMap blks) entries |
|---|
| 287 | |
|---|
| 288 | ----------------------------------------------------------------------------- |
|---|
| 289 | -- Backward analysis and rewriting: the interface |
|---|
| 290 | ----------------------------------------------------------------------------- |
|---|
| 291 | |
|---|
| 292 | data BwdPass n f |
|---|
| 293 | = BwdPass { bp_lattice :: DataflowLattice f |
|---|
| 294 | , bp_transfer :: BwdTransfer n f |
|---|
| 295 | , bp_rewrite :: BwdRewrite n f } |
|---|
| 296 | |
|---|
| 297 | newtype BwdTransfer n f |
|---|
| 298 | = BwdTransfers { getBTransfers :: |
|---|
| 299 | ( n C O -> f -> f |
|---|
| 300 | , n O O -> f -> f |
|---|
| 301 | , n O C -> FactBase f -> f |
|---|
| 302 | ) } |
|---|
| 303 | newtype BwdRewrite n f |
|---|
| 304 | = BwdRewrites { getBRewrites :: |
|---|
| 305 | ( n C O -> f -> Maybe (BwdRes n f C O) |
|---|
| 306 | , n O O -> f -> Maybe (BwdRes n f O O) |
|---|
| 307 | , n O C -> FactBase f -> Maybe (BwdRes n f O C) |
|---|
| 308 | ) } |
|---|
| 309 | data BwdRes n f e x = BwdRes (AGraph n e x) (BwdRewrite n f) |
|---|
| 310 | |
|---|
| 311 | mkBTransfer :: (n C O -> f -> f) -> (n O O -> f -> f) -> |
|---|
| 312 | (n O C -> FactBase f -> f) -> BwdTransfer n f |
|---|
| 313 | mkBTransfer f m l = BwdTransfers (f, m, l) |
|---|
| 314 | |
|---|
| 315 | mkBTransfer' :: (forall e x . n e x -> Fact x f -> f) -> BwdTransfer n f |
|---|
| 316 | mkBTransfer' f = BwdTransfers (f, f, f) |
|---|
| 317 | |
|---|
| 318 | mkBRewrite :: (n C O -> f -> Maybe (BwdRes n f C O)) |
|---|
| 319 | -> (n O O -> f -> Maybe (BwdRes n f O O)) |
|---|
| 320 | -> (n O C -> FactBase f -> Maybe (BwdRes n f O C)) |
|---|
| 321 | -> BwdRewrite n f |
|---|
| 322 | mkBRewrite f m l = BwdRewrites (f, m, l) |
|---|
| 323 | |
|---|
| 324 | mkBRewrite' :: (forall e x . n e x -> Fact x f -> Maybe (BwdRes n f e x)) -> BwdRewrite n f |
|---|
| 325 | mkBRewrite' f = BwdRewrites (f, f, f) |
|---|
| 326 | |
|---|
| 327 | |
|---|
| 328 | ----------------------------------------------------------------------------- |
|---|
| 329 | -- Backward implementation |
|---|
| 330 | ----------------------------------------------------------------------------- |
|---|
| 331 | |
|---|
| 332 | type ARB' n f thing e x |
|---|
| 333 | = BwdPass n f -> thing e x -> Fact x f -> FuelMonad (RG f n e x, f) |
|---|
| 334 | |
|---|
| 335 | type ARBX' n f thing e x |
|---|
| 336 | = BwdPass n f -> thing e x -> Fact x f -> FuelMonad (RG f n e x, Fact e f) |
|---|
| 337 | |
|---|
| 338 | type ARB thing n = forall f e x. ARB' n f thing e x |
|---|
| 339 | type ARBX thing n = forall f e x. ARBX' n f thing e x |
|---|
| 340 | |
|---|
| 341 | arbx :: Edges thing => ARB' n f thing C x -> ARBX' n f thing C x |
|---|
| 342 | arbx arb pass thing f = do { (rg, f) <- arb pass thing f |
|---|
| 343 | ; let fb = joinInFacts (bp_lattice pass) $ |
|---|
| 344 | mkFactBase [(entryLabel thing, f)] |
|---|
| 345 | ; return (rg, fb) } |
|---|
| 346 | |
|---|
| 347 | arbNode :: (Edges n, ShapeLifter e x) => ARB' n f n e x |
|---|
| 348 | -- Lifts (BwdTransfer,BwdRewrite) to ARB_Node; |
|---|
| 349 | -- this time we do rewriting as well. |
|---|
| 350 | -- The ARB_Graph parameters specifies what to do with the rewritten graph |
|---|
| 351 | arbNode pass node f |
|---|
| 352 | = do { mb_g <- withFuel (brewrite pass node f) |
|---|
| 353 | ; case mb_g of |
|---|
| 354 | Nothing -> return (rgunit entry_f (unit node), entry_f) |
|---|
| 355 | where entry_f = btransfer pass node f |
|---|
| 356 | Just (BwdRes ag rw) -> do { g <- graphOfAGraph ag |
|---|
| 357 | ; let pass' = pass { bp_rewrite = rw } |
|---|
| 358 | ; (g, f) <- arbGraph pass' g f |
|---|
| 359 | ; return (g, elower (bp_lattice pass) node f)} } |
|---|
| 360 | |
|---|
| 361 | arbBlock :: Edges n => ARB (Block n) n |
|---|
| 362 | -- Lift from nodes to blocks |
|---|
| 363 | arbBlock pass (BFirst node) = arbNode pass node |
|---|
| 364 | arbBlock pass (BMiddle node) = arbNode pass node |
|---|
| 365 | arbBlock pass (BLast node) = arbNode pass node |
|---|
| 366 | arbBlock pass (BCat b1 b2) = arbCat arbBlock arbBlock pass b1 b2 |
|---|
| 367 | arbBlock pass (BHead h n) = arbCat arbBlock arbNode pass h n |
|---|
| 368 | arbBlock pass (BTail n t) = arbCat arbNode arbBlock pass n t |
|---|
| 369 | arbBlock pass (BClosed h t) = arbCat arbBlock arbBlock pass h t |
|---|
| 370 | |
|---|
| 371 | arbCat :: (pass -> thing1 -> info1 -> FuelMonad (RG f n e a, info1')) |
|---|
| 372 | -> (pass -> thing2 -> info2 -> FuelMonad (RG f n a x, info1)) |
|---|
| 373 | -> (pass -> thing1 -> thing2 -> info2 -> FuelMonad (RG f n e x, info1')) |
|---|
| 374 | {-# INLINE arbCat #-} |
|---|
| 375 | arbCat arb1 arb2 pass thing1 thing2 f = do { (g2,f2) <- arb2 pass thing2 f |
|---|
| 376 | ; (g1,f1) <- arb1 pass thing1 f2 |
|---|
| 377 | ; return (g1 `rgCat` g2, f1) } |
|---|
| 378 | |
|---|
| 379 | arbBody :: Edges n |
|---|
| 380 | => BwdPass n f -> Body n -> FactBase f |
|---|
| 381 | -> FuelMonad (RG f n C C, FactBase f) |
|---|
| 382 | arbBody pass blocks init_fbase |
|---|
| 383 | = fixpoint False (bp_lattice pass) do_block init_fbase $ |
|---|
| 384 | backwardBlockList blocks |
|---|
| 385 | where |
|---|
| 386 | do_block b f = do (g, f) <- arbBlock pass b f |
|---|
| 387 | return (g, [(entryLabel b, f)]) |
|---|
| 388 | |
|---|
| 389 | arbGraph :: Edges n => ARBX (Graph n) n |
|---|
| 390 | arbGraph _ GNil = \f -> return (rgnil, f) |
|---|
| 391 | arbGraph pass (GUnit blk) = arbBlock pass blk |
|---|
| 392 | arbGraph pass (GMany NothingO body NothingO) = arbBody pass body |
|---|
| 393 | arbGraph pass (GMany NothingO body (JustO exit)) = |
|---|
| 394 | arbCat arbBody (arbx arbBlock) pass body exit |
|---|
| 395 | arbGraph pass (GMany (JustO entry) body NothingO) = |
|---|
| 396 | arbCat arbBlock arbBody pass entry body |
|---|
| 397 | arbGraph pass (GMany (JustO entry) body (JustO exit)) = |
|---|
| 398 | arbCat arbeb (arbx arbBlock) pass (entry, body) exit |
|---|
| 399 | where arbeb pass = uncurry $ arbCat arbBlock arbBody pass |
|---|
| 400 | |
|---|
| 401 | |
|---|
| 402 | backwardBlockList :: Edges n => Body n -> [Block n C C] |
|---|
| 403 | -- This produces a list of blocks in order suitable for backward analysis, |
|---|
| 404 | -- along with the list of Labels it may depend on for facts. |
|---|
| 405 | backwardBlockList body = reachable ++ missing |
|---|
| 406 | where reachable = reverse $ forwardBlockList entries body |
|---|
| 407 | entries = externalEntryLabels body |
|---|
| 408 | all = bodyList body |
|---|
| 409 | missingLabels = |
|---|
| 410 | mkLabelSet (map fst all) `minusLabelSet` |
|---|
| 411 | mkLabelSet (map entryLabel reachable) |
|---|
| 412 | missing = map snd $ filter (flip elemLabelSet missingLabels . fst) all |
|---|
| 413 | |
|---|
| 414 | {- |
|---|
| 415 | |
|---|
| 416 | The forward and backward dataflow analyses now use postorder depth-first |
|---|
| 417 | order for faster convergence. |
|---|
| 418 | |
|---|
| 419 | The forward and backward cases are not dual. In the forward case, the |
|---|
| 420 | entry points are known, and one simply traverses the body blocks from |
|---|
| 421 | those points. In the backward case, something is known about the exit |
|---|
| 422 | points, but this information is essentially useless, because we don't |
|---|
| 423 | actually have a dual graph (that is, one with edges reversed) to |
|---|
| 424 | compute with. (Even if we did have a dual graph, it would not avail |
|---|
| 425 | us---a backward analysis must include reachable blocks that don't |
|---|
| 426 | reach the exit, as in a procedure that loops forever and has side |
|---|
| 427 | effects.) |
|---|
| 428 | |
|---|
| 429 | Since in the general case, no information is available about entry |
|---|
| 430 | points, I have put in a horrible hack. First, I assume that every |
|---|
| 431 | label defined but not used is an entry point. Then, because an entry |
|---|
| 432 | point might also be a loop header, I add, in arbitrary order, all the |
|---|
| 433 | remaining "missing" blocks. Needless to say, I am not pleased. |
|---|
| 434 | I am not satisfied. I am not Senator Morgan. |
|---|
| 435 | |
|---|
| 436 | Wait! I believe that the Right Thing here is to require that anyone |
|---|
| 437 | wishing to analyze a graph closed at the entry provide a way of |
|---|
| 438 | determining the entry points, if any, of that graph. This requirement |
|---|
| 439 | can apply equally to forward and backward analyses; I believe that |
|---|
| 440 | using the input FactBase to determine the entry points of a closed |
|---|
| 441 | graph is *also* a hack. |
|---|
| 442 | |
|---|
| 443 | NR |
|---|
| 444 | |
|---|
| 445 | -} |
|---|
| 446 | |
|---|
| 447 | |
|---|
| 448 | analyzeAndRewriteBwd |
|---|
| 449 | :: forall n f. Edges n |
|---|
| 450 | => BwdPass n f |
|---|
| 451 | -> Body n -> FactBase f |
|---|
| 452 | -> FuelMonad (Body n, FactBase f) |
|---|
| 453 | |
|---|
| 454 | analyzeAndRewriteBwd pass body facts |
|---|
| 455 | = do { (rg, _) <- arbBody pass body facts |
|---|
| 456 | ; return (normaliseBody rg) } |
|---|
| 457 | |
|---|
| 458 | -- | if the graph being analyzed is open at the exit, I don't |
|---|
| 459 | -- quite understand the implications of possible other exits |
|---|
| 460 | analyzeAndRewriteBwd' |
|---|
| 461 | :: forall n f e x. Edges n |
|---|
| 462 | => BwdPass n f |
|---|
| 463 | -> Graph n e x -> Fact x f |
|---|
| 464 | -> FuelMonad (Graph n e x, FactBase f, MaybeO e f) |
|---|
| 465 | analyzeAndRewriteBwd' pass g f = |
|---|
| 466 | do (rg, fout) <- arbGraph pass g f |
|---|
| 467 | let (g', fb) = normalizeGraph rg |
|---|
| 468 | return (g', fb, distinguishedEntryFact g' fout) |
|---|
| 469 | |
|---|
| 470 | distinguishedEntryFact :: forall n e x f . Graph n e x -> Fact e f -> MaybeO e f |
|---|
| 471 | distinguishedEntryFact g f = maybe g |
|---|
| 472 | where maybe :: Graph n e x -> MaybeO e f |
|---|
| 473 | maybe GNil = JustO f |
|---|
| 474 | maybe (GUnit {}) = JustO f |
|---|
| 475 | maybe (GMany e _ _) = case e of NothingO -> NothingO |
|---|
| 476 | JustO _ -> JustO f |
|---|
| 477 | |
|---|
| 478 | ----------------------------------------------------------------------------- |
|---|
| 479 | -- fixpoint: finding fixed points |
|---|
| 480 | ----------------------------------------------------------------------------- |
|---|
| 481 | |
|---|
| 482 | data TxFactBase n f |
|---|
| 483 | = TxFB { tfb_fbase :: FactBase f |
|---|
| 484 | , tfb_rg :: RG f n C C -- Transformed blocks |
|---|
| 485 | , tfb_cha :: ChangeFlag |
|---|
| 486 | , tfb_lbls :: LabelSet } |
|---|
| 487 | -- Note [TxFactBase change flag] |
|---|
| 488 | -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 489 | -- Set the tfb_cha flag iff |
|---|
| 490 | -- (a) the fact in tfb_fbase for or a block L changes |
|---|
| 491 | -- (b) L is in tfb_lbls. |
|---|
| 492 | -- The tfb_lbls are all Labels of the *original* |
|---|
| 493 | -- (not transformed) blocks |
|---|
| 494 | |
|---|
| 495 | updateFact :: DataflowLattice f -> LabelSet -> (Label, f) |
|---|
| 496 | -> (ChangeFlag, FactBase f) |
|---|
| 497 | -> (ChangeFlag, FactBase f) |
|---|
| 498 | -- See Note [TxFactBase change flag] |
|---|
| 499 | updateFact lat lbls (lbl, new_fact) (cha, fbase) |
|---|
| 500 | | NoChange <- cha2 = (cha, fbase) |
|---|
| 501 | | lbl `elemLabelSet` lbls = (SomeChange, new_fbase) |
|---|
| 502 | | otherwise = (cha, new_fbase) |
|---|
| 503 | where |
|---|
| 504 | (cha2, res_fact) -- Note [Unreachable blocks] |
|---|
| 505 | = case lookupFact fbase lbl of |
|---|
| 506 | Nothing -> (SomeChange, snd $ join $ fact_bot lat) -- Note [Unreachable blocks] |
|---|
| 507 | Just old_fact -> join old_fact |
|---|
| 508 | where join old_fact = fact_extend lat lbl (OldFact old_fact) (NewFact new_fact) |
|---|
| 509 | new_fbase = extendFactBase fbase lbl res_fact |
|---|
| 510 | |
|---|
| 511 | fixpoint :: forall block n f. Edges (block n) |
|---|
| 512 | => Bool -- Going forwards? |
|---|
| 513 | -> DataflowLattice f |
|---|
| 514 | -> (block n C C -> FactBase f |
|---|
| 515 | -> FuelMonad (RG f n C C, [(Label, f)])) |
|---|
| 516 | -> FactBase f |
|---|
| 517 | -> [block n C C] |
|---|
| 518 | -> FuelMonad (RG f n C C, FactBase f) |
|---|
| 519 | fixpoint is_fwd lat do_block init_fbase untagged_blocks |
|---|
| 520 | = do { fuel <- getFuel |
|---|
| 521 | ; tx_fb <- loop fuel init_fbase |
|---|
| 522 | ; return (tfb_rg tx_fb, |
|---|
| 523 | tfb_fbase tx_fb `delFromFactBase` map fst blocks) } |
|---|
| 524 | -- The successors of the Graph are the the Labels for which |
|---|
| 525 | -- we have facts, that are *not* in the blocks of the graph |
|---|
| 526 | where |
|---|
| 527 | blocks = map tag untagged_blocks |
|---|
| 528 | where tag b = ((entryLabel b, b), if is_fwd then [entryLabel b] else successors b) |
|---|
| 529 | |
|---|
| 530 | tx_blocks :: [((Label, block n C C), [Label])] -- I do not understand this type |
|---|
| 531 | -> TxFactBase n f -> FuelMonad (TxFactBase n f) |
|---|
| 532 | tx_blocks [] tx_fb = return tx_fb |
|---|
| 533 | tx_blocks (((lbl,blk), deps):bs) tx_fb = tx_block lbl blk deps tx_fb >>= tx_blocks bs |
|---|
| 534 | -- "deps" == Labels the block may _depend_ upon for facts |
|---|
| 535 | |
|---|
| 536 | tx_block :: Label -> block n C C -> [Label] |
|---|
| 537 | -> TxFactBase n f -> FuelMonad (TxFactBase n f) |
|---|
| 538 | tx_block lbl blk deps tx_fb@(TxFB { tfb_fbase = fbase, tfb_lbls = lbls |
|---|
| 539 | , tfb_rg = blks, tfb_cha = cha }) |
|---|
| 540 | | is_fwd && not (lbl `elemFactBase` fbase) |
|---|
| 541 | = return tx_fb {tfb_lbls = lbls `unionLabelSet` mkLabelSet deps} -- Note [Unreachable blocks] |
|---|
| 542 | | otherwise |
|---|
| 543 | = do { (rg, out_facts) <- do_block blk fbase |
|---|
| 544 | ; let (cha',fbase') |
|---|
| 545 | = foldr (updateFact lat lbls) (cha,fbase) out_facts |
|---|
| 546 | lbls' = lbls `unionLabelSet` mkLabelSet deps |
|---|
| 547 | ; return (TxFB { tfb_lbls = lbls' |
|---|
| 548 | , tfb_rg = rg `rgCat` blks |
|---|
| 549 | , tfb_fbase = fbase', tfb_cha = cha' }) } |
|---|
| 550 | |
|---|
| 551 | loop :: Fuel -> FactBase f -> FuelMonad (TxFactBase n f) |
|---|
| 552 | loop fuel fbase |
|---|
| 553 | = do { let init_tx_fb = TxFB { tfb_fbase = fbase |
|---|
| 554 | , tfb_cha = NoChange |
|---|
| 555 | , tfb_rg = rgnilC |
|---|
| 556 | , tfb_lbls = emptyLabelSet } |
|---|
| 557 | ; tx_fb <- tx_blocks blocks init_tx_fb |
|---|
| 558 | ; case tfb_cha tx_fb of |
|---|
| 559 | NoChange -> return tx_fb |
|---|
| 560 | SomeChange -> do { setFuel fuel |
|---|
| 561 | ; loop fuel (tfb_fbase tx_fb) } } |
|---|
| 562 | |
|---|
| 563 | {- Note [Unreachable blocks] |
|---|
| 564 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 565 | A block that is not in the domain of tfb_fbase is "currently unreachable". |
|---|
| 566 | A currently-unreachable block is not even analyzed. Reason: consider |
|---|
| 567 | constant prop and this graph, with entry point L1: |
|---|
| 568 | L1: x:=3; goto L4 |
|---|
| 569 | L2: x:=4; goto L4 |
|---|
| 570 | L4: if x>3 goto L2 else goto L5 |
|---|
| 571 | Here L2 is actually unreachable, but if we process it with bottom input fact, |
|---|
| 572 | we'll propagate (x=4) to L4, and nuke the otherwise-good rewriting of L4. |
|---|
| 573 | |
|---|
| 574 | * If a currently-unreachable block is not analyzed, then its rewritten |
|---|
| 575 | graph will not be accumulated in tfb_rg. And that is good: |
|---|
| 576 | unreachable blocks simply do not appear in the output. |
|---|
| 577 | |
|---|
| 578 | * Note that clients must be careful to provide a fact (even if bottom) |
|---|
| 579 | for each entry point. Otherwise useful blocks may be garbage collected. |
|---|
| 580 | |
|---|
| 581 | * Note that updateFact must set the change-flag if a label goes from |
|---|
| 582 | not-in-fbase to in-fbase, even if its fact is bottom. In effect the |
|---|
| 583 | real fact lattice is |
|---|
| 584 | UNR |
|---|
| 585 | bottom |
|---|
| 586 | the points above bottom |
|---|
| 587 | |
|---|
| 588 | * Even if the fact is going from UNR to bottom, we still call the |
|---|
| 589 | client's fact_extend function because it might give the client |
|---|
| 590 | some useful debugging information. |
|---|
| 591 | |
|---|
| 592 | * All of this only applies for *forward* fixpoints. For the backward |
|---|
| 593 | case we must treat every block as reachable; it might finish with a |
|---|
| 594 | 'return', and therefore have no successors, for example. |
|---|
| 595 | -} |
|---|
| 596 | |
|---|
| 597 | ----------------------------------------------------------------------------- |
|---|
| 598 | -- RG: an internal data type for graphs under construction |
|---|
| 599 | -- TOTALLY internal to Hoopl; each block carries its fact |
|---|
| 600 | ----------------------------------------------------------------------------- |
|---|
| 601 | |
|---|
| 602 | type RG f n e x = Graph' (FBlock f) n e x |
|---|
| 603 | data FBlock f n e x = FBlock f (Block n e x) |
|---|
| 604 | |
|---|
| 605 | --- constructors |
|---|
| 606 | |
|---|
| 607 | rgnil :: RG f n O O |
|---|
| 608 | rgnilC :: RG f n C C |
|---|
| 609 | rgunit :: f -> Block n e x -> RG f n e x |
|---|
| 610 | rgCat :: RG f n e a -> RG f n a x -> RG f n e x |
|---|
| 611 | |
|---|
| 612 | ---- observers |
|---|
| 613 | |
|---|
| 614 | type BodyWithFacts n f = (Body n, FactBase f) |
|---|
| 615 | type GraphWithFacts n f e x = (Graph n e x, FactBase f) |
|---|
| 616 | -- A Graph together with the facts for that graph |
|---|
| 617 | -- The domains of the two maps should be identical |
|---|
| 618 | |
|---|
| 619 | normalizeGraph :: forall n f e x . |
|---|
| 620 | Edges n => RG f n e x -> GraphWithFacts n f e x |
|---|
| 621 | normaliseBody :: Edges n => RG f n C C -> BodyWithFacts n f |
|---|
| 622 | |
|---|
| 623 | normalizeGraph g = (graphMapBlocks dropFact g, facts g) |
|---|
| 624 | where dropFact (FBlock _ b) = b |
|---|
| 625 | facts :: RG f n e x -> FactBase f |
|---|
| 626 | facts GNil = noFacts |
|---|
| 627 | facts (GUnit _) = noFacts |
|---|
| 628 | facts (GMany _ body exit) = bodyFacts body `unionFactBase` exitFacts exit |
|---|
| 629 | exitFacts :: MaybeO x (FBlock f n C O) -> FactBase f |
|---|
| 630 | exitFacts NothingO = noFacts |
|---|
| 631 | exitFacts (JustO (FBlock f b)) = mkFactBase [(entryLabel b, f)] |
|---|
| 632 | bodyFacts :: Body' (FBlock f) n -> FactBase f |
|---|
| 633 | bodyFacts (BodyUnit (FBlock f b)) = mkFactBase [(entryLabel b, f)] |
|---|
| 634 | bodyFacts (b1 `BodyCat` b2) = bodyFacts b1 `unionFactBase` bodyFacts b2 |
|---|
| 635 | |
|---|
| 636 | normaliseBody rg = (body, fact_base) |
|---|
| 637 | where (GMany _ body _, fact_base) = normalizeGraph rg |
|---|
| 638 | |
|---|
| 639 | --- implementation of the constructors (boring) |
|---|
| 640 | |
|---|
| 641 | rgnil = GNil |
|---|
| 642 | rgnilC = GMany NothingO BodyEmpty NothingO |
|---|
| 643 | |
|---|
| 644 | rgunit f b@(BFirst {}) = gUnitCO (FBlock f b) |
|---|
| 645 | rgunit f b@(BMiddle {}) = gUnitOO (FBlock f b) |
|---|
| 646 | rgunit f b@(BLast {}) = gUnitOC (FBlock f b) |
|---|
| 647 | rgunit f b@(BCat {}) = gUnitOO (FBlock f b) |
|---|
| 648 | rgunit f b@(BHead {}) = gUnitCO (FBlock f b) |
|---|
| 649 | rgunit f b@(BTail {}) = gUnitOC (FBlock f b) |
|---|
| 650 | rgunit f b@(BClosed {}) = gUnitCC (FBlock f b) |
|---|
| 651 | |
|---|
| 652 | rgCat = U.splice fzCat |
|---|
| 653 | where fzCat (FBlock f b1) (FBlock _ b2) = FBlock f (b1 `U.cat` b2) |
|---|
| 654 | |
|---|
| 655 | ---------------------------------------------------------------- |
|---|
| 656 | -- Utilities |
|---|
| 657 | ---------------------------------------------------------------- |
|---|
| 658 | |
|---|
| 659 | -- Lifting based on shape: |
|---|
| 660 | -- - from nodes to blocks |
|---|
| 661 | -- - from facts to fact-like things |
|---|
| 662 | -- Lowering back: |
|---|
| 663 | -- - from fact-like things to facts |
|---|
| 664 | -- Note that the latter two functions depend only on the entry shape. |
|---|
| 665 | class ShapeLifter e x where |
|---|
| 666 | unit :: n e x -> Block n e x |
|---|
| 667 | elift :: Edges n => n e x -> f -> Fact e f |
|---|
| 668 | elower :: Edges n => DataflowLattice f -> n e x -> Fact e f -> f |
|---|
| 669 | ftransfer :: FwdPass n f -> n e x -> f -> Fact x f |
|---|
| 670 | btransfer :: BwdPass n f -> n e x -> Fact x f -> f |
|---|
| 671 | frewrite :: FwdPass n f -> n e x -> f -> Maybe (FwdRes n f e x) |
|---|
| 672 | brewrite :: BwdPass n f -> n e x -> Fact x f -> Maybe (BwdRes n f e x) |
|---|
| 673 | |
|---|
| 674 | instance ShapeLifter C O where |
|---|
| 675 | unit = BFirst |
|---|
| 676 | elift n f = mkFactBase [(entryLabel n, f)] |
|---|
| 677 | elower lat n fb = getFact lat (entryLabel n) fb |
|---|
| 678 | ftransfer (FwdPass {fp_transfer = FwdTransfers (ft, _, _)}) n f = ft n f |
|---|
| 679 | btransfer (BwdPass {bp_transfer = BwdTransfers (bt, _, _)}) n f = bt n f |
|---|
| 680 | frewrite (FwdPass {fp_rewrite = FwdRewrites (fr, _, _)}) n f = fr n f |
|---|
| 681 | brewrite (BwdPass {bp_rewrite = BwdRewrites (br, _, _)}) n f = br n f |
|---|
| 682 | |
|---|
| 683 | instance ShapeLifter O O where |
|---|
| 684 | unit = BMiddle |
|---|
| 685 | elift _ f = f |
|---|
| 686 | elower _ _ f = f |
|---|
| 687 | ftransfer (FwdPass {fp_transfer = FwdTransfers (_, ft, _)}) n f = ft n f |
|---|
| 688 | btransfer (BwdPass {bp_transfer = BwdTransfers (_, bt, _)}) n f = bt n f |
|---|
| 689 | frewrite (FwdPass {fp_rewrite = FwdRewrites (_, fr, _)}) n f = fr n f |
|---|
| 690 | brewrite (BwdPass {bp_rewrite = BwdRewrites (_, br, _)}) n f = br n f |
|---|
| 691 | |
|---|
| 692 | instance ShapeLifter O C where |
|---|
| 693 | unit = BLast |
|---|
| 694 | elift _ f = f |
|---|
| 695 | elower _ _ f = f |
|---|
| 696 | ftransfer (FwdPass {fp_transfer = FwdTransfers (_, _, ft)}) n f = ft n f |
|---|
| 697 | btransfer (BwdPass {bp_transfer = BwdTransfers (_, _, bt)}) n f = bt n f |
|---|
| 698 | frewrite (FwdPass {fp_rewrite = FwdRewrites (_, _, fr)}) n f = fr n f |
|---|
| 699 | brewrite (BwdPass {bp_rewrite = BwdRewrites (_, _, br)}) n f = br n f |
|---|
| 700 | |
|---|
| 701 | -- Fact lookup: the fact `orelse` bottom |
|---|
| 702 | lookupF :: FwdPass n f -> Label -> FactBase f -> f |
|---|
| 703 | lookupF = getFact . fp_lattice |
|---|
| 704 | |
|---|
| 705 | getFact :: DataflowLattice f -> Label -> FactBase f -> f |
|---|
| 706 | getFact lat l fb = case lookupFact fb l of Just f -> f |
|---|
| 707 | Nothing -> fact_bot lat |
|---|