Ticket #4020: DataflowNest.hs

File DataflowNest.hs, 28.9 KB (added by nr, 3 years ago)

Example of where nested type synonyms would be lovely (line 202)

Line 
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~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6Hoopl7 has the following major chages
7
8a) GMany has symmetric entry and exit
9b) GMany closed-entry does not record a BlockId
10c) GMany open-exit does not record a BlockId
11d) The body of a GMany is called Body
12e) A Body is just a list of blocks, not a map. I've argued
13   elsewhere that this is consistent with (c)
14
15A consequence is that Graph is no longer an instance of Edges,
16but nevertheless I managed to keep the ARF and ARB signatures
17nice and uniform.
18
19This 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
58module 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  )
68where
69
70import Data.Maybe
71
72import Compiler.Hoopl.Fuel
73import Compiler.Hoopl.Graph
74import Compiler.Hoopl.MkGraph
75import qualified Compiler.Hoopl.GraphUtil as U
76import Compiler.Hoopl.Label
77import Compiler.Hoopl.Util
78
79-----------------------------------------------------------------------------
80--              DataflowLattice
81-----------------------------------------------------------------------------
82
83data 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
95type JoinFun a = Label -> OldFact a -> NewFact a -> (ChangeFlag, a)
96  -- the label argument is for debugging purposes only
97newtype OldFact a = OldFact a
98newtype NewFact a = NewFact a
99
100data ChangeFlag = NoChange | SomeChange deriving (Eq, Ord)
101changeIf :: Bool -> ChangeFlag
102changeIf changed = if changed then SomeChange else NoChange
103
104
105-----------------------------------------------------------------------------
106--              Analyze and rewrite forward: the interface
107-----------------------------------------------------------------------------
108
109data FwdPass n f
110  = FwdPass { fp_lattice  :: DataflowLattice f
111            , fp_transfer :: FwdTransfer n f
112            , fp_rewrite  :: FwdRewrite n f }
113
114newtype 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
121newtype 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                    ) }
127data 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
130mkFTransfer :: (n C O -> f -> f)
131            -> (n O O -> f -> f)
132            -> (n O C -> f -> FactBase f)
133            -> FwdTransfer n f
134mkFTransfer f m l = FwdTransfers (f, m, l)
135
136mkFTransfer' :: (forall e x . n e x -> f -> Fact x f) -> FwdTransfer n f
137mkFTransfer' f = FwdTransfers (f, f, f)
138
139mkFRewrite :: (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
143mkFRewrite f m l = FwdRewrites (f, m, l)
144
145mkFRewrite' :: (forall e x . n e x -> f -> Maybe (FwdRes n f e x)) -> FwdRewrite n f
146mkFRewrite' f = FwdRewrites (f, f, f)
147
148
149type family   Fact x f :: *
150type instance Fact C f = FactBase f
151type instance Fact O f = f
152
153analyzeAndRewriteFwd
154   :: forall n f. Edges n
155   => FwdPass n f
156   -> Body n -> FactBase f
157   -> FuelMonad (Body n, FactBase f)
158
159analyzeAndRewriteFwd 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...
167analyzeAndRewriteFwd'
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)
172analyzeAndRewriteFwd' 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
177distinguishedExitFact :: forall n e x f . Graph n e x -> Fact x f -> MaybeO x f
178distinguishedExitFact 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
189type 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
193type 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
197type ARFt  n f thing = forall e x . ARF'  n f thing e x
198type ARFXt n f thing = forall e x . ARFX' n f thing e x
199
200arfGraph :: forall n f . Edges n => FwdPass n f -> ARFXt n f (Graph n)
201arfGraph 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.
277joinInFacts :: DataflowLattice f -> FactBase f -> FactBase f
278joinInFacts (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
282forwardBlockList :: (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.
286forwardBlockList entries blks = postorder_dfs_from (bodyMap blks) entries
287
288-----------------------------------------------------------------------------
289--              Backward analysis and rewriting: the interface
290-----------------------------------------------------------------------------
291
292data BwdPass n f
293  = BwdPass { bp_lattice  :: DataflowLattice f
294            , bp_transfer :: BwdTransfer n f
295            , bp_rewrite  :: BwdRewrite n f }
296
297newtype 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                     ) }
303newtype 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                    ) }
309data BwdRes n f e x = BwdRes (AGraph n e x) (BwdRewrite n f)
310
311mkBTransfer :: (n C O -> f -> f) -> (n O O -> f -> f) ->
312               (n O C -> FactBase f -> f) -> BwdTransfer n f
313mkBTransfer f m l = BwdTransfers (f, m, l)
314
315mkBTransfer' :: (forall e x . n e x -> Fact x f -> f) -> BwdTransfer n f
316mkBTransfer' f = BwdTransfers (f, f, f)
317
318mkBRewrite :: (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
322mkBRewrite f m l = BwdRewrites (f, m, l)
323
324mkBRewrite' :: (forall e x . n e x -> Fact x f -> Maybe (BwdRes n f e x)) -> BwdRewrite n f
325mkBRewrite' f = BwdRewrites (f, f, f)
326
327
328-----------------------------------------------------------------------------
329--              Backward implementation
330-----------------------------------------------------------------------------
331
332type ARB' n f thing e x
333  = BwdPass n f -> thing e x -> Fact x f -> FuelMonad (RG f n e x, f)
334
335type 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
338type ARB  thing n = forall f e x. ARB'  n f thing e x
339type ARBX thing n = forall f e x. ARBX' n f thing e x
340
341arbx :: Edges thing => ARB' n f thing C x -> ARBX' n f thing C x
342arbx 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
347arbNode :: (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
351arbNode 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
361arbBlock :: Edges n => ARB (Block n) n
362-- Lift from nodes to blocks
363arbBlock pass (BFirst  node)  = arbNode pass node
364arbBlock pass (BMiddle node)  = arbNode pass node
365arbBlock pass (BLast   node)  = arbNode pass node
366arbBlock pass (BCat b1 b2)    = arbCat arbBlock arbBlock pass b1 b2
367arbBlock pass (BHead h n)     = arbCat arbBlock arbNode  pass h n
368arbBlock pass (BTail n t)     = arbCat arbNode  arbBlock pass n t
369arbBlock pass (BClosed h t)   = arbCat arbBlock arbBlock pass h t
370
371arbCat :: (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 #-}
375arbCat 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
379arbBody :: Edges n
380        => BwdPass n f -> Body n -> FactBase f
381        -> FuelMonad (RG f n C C, FactBase f)
382arbBody 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
389arbGraph :: Edges n => ARBX (Graph n) n
390arbGraph _    GNil        = \f -> return (rgnil, f)
391arbGraph pass (GUnit blk) = arbBlock pass blk
392arbGraph pass (GMany NothingO body NothingO) = arbBody pass body
393arbGraph pass (GMany NothingO body (JustO exit)) =
394  arbCat arbBody (arbx arbBlock) pass body exit
395arbGraph pass (GMany (JustO entry) body NothingO) =
396  arbCat arbBlock arbBody pass entry body
397arbGraph 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
402backwardBlockList :: 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.
405backwardBlockList 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
416The forward and backward dataflow analyses now use postorder depth-first
417order for faster convergence.
418
419The forward and backward cases are not dual.  In the forward case, the
420entry points are known, and one simply traverses the body blocks from
421those points.  In the backward case, something is known about the exit
422points, but this information is essentially useless, because we don't
423actually have a dual graph (that is, one with edges reversed) to
424compute with.  (Even if we did have a dual graph, it would not avail
425us---a backward analysis must include reachable blocks that don't
426reach the exit, as in a procedure that loops forever and has side
427effects.)
428
429Since in the general case, no information is available about entry
430points, I have put in a horrible hack.  First, I assume that every
431label defined but not used is an entry point.  Then, because an entry
432point might also be a loop header, I add, in arbitrary order, all the
433remaining "missing" blocks.  Needless to say, I am not pleased. 
434I am not satisfied.  I am not Senator Morgan.
435
436Wait! I believe that the Right Thing here is to require that anyone
437wishing to analyze a graph closed at the entry provide a way of
438determining the entry points, if any, of that graph.  This requirement
439can apply equally to forward and backward analyses; I believe that
440using the input FactBase to determine the entry points of a closed
441graph is *also* a hack.
442
443NR
444
445-}
446
447
448analyzeAndRewriteBwd
449   :: forall n f. Edges n
450   => BwdPass n f
451   -> Body n -> FactBase f
452   -> FuelMonad (Body n, FactBase f)
453
454analyzeAndRewriteBwd 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
460analyzeAndRewriteBwd'
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)
465analyzeAndRewriteBwd' 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
470distinguishedEntryFact :: forall n e x f . Graph n e x -> Fact e f -> MaybeO e f
471distinguishedEntryFact 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
482data 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
495updateFact :: DataflowLattice f -> LabelSet -> (Label, f)
496           -> (ChangeFlag, FactBase f) 
497           -> (ChangeFlag, FactBase f)
498-- See Note [TxFactBase change flag]
499updateFact 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
511fixpoint :: 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)
519fixpoint 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~~~~~~~~~~~~~~~~~~~~~~~~~~~~
565A block that is not in the domain of tfb_fbase is "currently unreachable".
566A currently-unreachable block is not even analyzed.  Reason: consider
567constant 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
571Here L2 is actually unreachable, but if we process it with bottom input fact,
572we'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
602type RG      f n e x = Graph' (FBlock f) n e x
603data FBlock f n e x = FBlock f (Block n e x)
604
605--- constructors
606
607rgnil  :: RG f n O O
608rgnilC :: RG f n C C
609rgunit :: f -> Block n e x -> RG f n e x
610rgCat  :: RG f n e a -> RG f n a x -> RG f n e x
611
612---- observers
613
614type BodyWithFacts  n f     = (Body n, FactBase f)
615type 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
619normalizeGraph :: forall n f e x .
620                  Edges n => RG f n e x -> GraphWithFacts n f e x
621normaliseBody  :: Edges n => RG f n C C -> BodyWithFacts n f
622
623normalizeGraph 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
636normaliseBody rg = (body, fact_base)
637  where (GMany _ body _, fact_base) = normalizeGraph rg
638
639--- implementation of the constructors (boring)
640
641rgnil  = GNil
642rgnilC = GMany NothingO BodyEmpty NothingO
643
644rgunit f b@(BFirst  {}) = gUnitCO (FBlock f b)
645rgunit f b@(BMiddle {}) = gUnitOO (FBlock f b)
646rgunit f b@(BLast   {}) = gUnitOC (FBlock f b)
647rgunit f b@(BCat {})    = gUnitOO (FBlock f b)
648rgunit f b@(BHead {})   = gUnitCO (FBlock f b)
649rgunit f b@(BTail {})   = gUnitOC (FBlock f b)
650rgunit f b@(BClosed {}) = gUnitCC (FBlock f b)
651
652rgCat = 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.
665class 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
674instance 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
683instance 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
692instance 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
702lookupF :: FwdPass n f -> Label -> FactBase f -> f
703lookupF = getFact . fp_lattice
704
705getFact  :: DataflowLattice f -> Label -> FactBase f -> f
706getFact lat l fb = case lookupFact fb l of Just  f -> f
707                                           Nothing -> fact_bot lat