1 {-# LANGUAGE MultiParamTypeClasses, ScopedTypeVariables #-}
2 {-# OPTIONS -fglasgow-exts #-}
3 -- -fglagow-exts for kind signatures
6 ( DebugNodes(), RewritingDepth(..), LastOutFacts(..)
7 , zdfSolveFrom, zdfRewriteFrom
9 , ForwardTransfers(..), BackwardTransfers(..)
10 , ForwardRewrites(..), BackwardRewrites(..)
11 , ForwardFixedPoint, BackwardFixedPoint
15 , zdfDecoratedGraph -- not yet implemented
18 , zdfBRewriteFromL, zdfFRewriteFromL
25 import OptimizationFuel as F
28 import qualified ZipCfg as G
40 This module implements two useful tools:
42 1. An iterative solver for dataflow problems
44 2. The combined dataflow-analysis-and-transformation framework
45 described by Lerner, Grove, and Chambers in their excellent
46 2002 POPL paper (http://tinyurl.com/3zycbr or
47 http://tinyurl.com/3pnscd).
49 Each tool comes in two flavors: one for forward dataflow problems
50 and one for backward dataflow problems.
52 We quote the paper above:
54 Dataflow analyses can have mutually beneficial interactions.
55 Previous efforts to exploit these interactions have either
56 (1) iteratively performed each individual analysis until no
57 further improvements are discovered or (2) developed "super-
58 analyses" that manually combine conceptually separate anal-
59 yses. We have devised a new approach that allows anal-
60 yses to be defined independently while still enabling them
61 to be combined automatically and profitably. Our approach
62 avoids the loss of precision associated with iterating indi-
63 vidual analyses and the implementation difficulties of man-
64 ually writing a super-analysis.
66 The key idea is to provide at each CFG node not only a dataflow
67 transfer function but also a rewriting function that has the option to
68 replace the node with a new (possibly empty) graph. The rewriting
69 function takes a dataflow fact as input, and the fact is used to
70 justify any rewriting. For example, in a backward problem, the fact
71 that variable x is dead can be used to justify rewriting node
73 to the empty graph. In a forward problem, the fact that x == 7 can
74 be used to justify rewriting node
78 which in turn will be analyzed and produce a new fact:
81 In its most general form, this module takes as input graph, transfer
82 equations, rewrites, and an initial set of dataflow facts, and
83 iteratively computes a new graph and a new set of dataflow facts such
85 * The set of facts is a fixed point of the transfer equations
86 * The graph has been rewritten as much as is consistent with
87 the given facts and requested rewriting depth (see below)
88 N.B. 'A set of facts' is shorthand for 'A finite map from CFG label to fact'.
90 The types of transfer equations, rewrites, and fixed points are
91 different for forward and backward problems. To avoid cluttering the
92 name space with two versions of every names, other names such as
93 zdfSolveFrom are overloaded to work in both forward or backward
94 directions. This design decision is based on experience with the
95 predecessor module, now called ZipDataflow0 and destined for the bit bucket.
98 This module is deliberately very abstract. It is a completely general
99 framework and well-nigh impossible to understand in isolation. The
100 cautious reader will begin with some concrete examples in the form of
101 clients. NR recommends
103 CmmLiveZ A simple liveness analysis
105 CmmSpillReload.removeDeadAssignmentsAndReloads
106 A piece of spaghetti to pull on, which leads to
107 - a two-part liveness analysis that tracks
108 variables live in registers and live on the stack
109 - elimination of assignments to dead variables
110 - elimination of redundant reloads
112 Even hearty souls should avoid the CmmProcPointZ client, at least for
118 {- ============ TRANSFER FUNCTIONS AND REWRITES =========== -}
120 -- | For a backward transfer, you're given the fact on a node's
121 -- outedge and you compute the fact on the inedge. Facts have type 'a'.
122 -- A last node may have multiple outedges, each pointing to a labelled
123 -- block, so instead of a fact it is given a mapping from BlockId to fact.
125 data BackwardTransfers middle last a = BackwardTransfers
126 { bt_first_in :: a -> BlockId -> a
127 , bt_middle_in :: a -> middle -> a
128 , bt_last_in :: (BlockId -> a) -> last -> a
131 -- | For a forward transfer, you're given the fact on a node's
132 -- inedge and you compute the fact on the outedge. Because a last node
133 -- may have multiple outedges, each pointing to a labelled
134 -- block, so instead of a fact it produces a list of (BlockId, fact) pairs.
136 data ForwardTransfers middle last a = ForwardTransfers
137 { ft_first_out :: a -> BlockId -> a
138 , ft_middle_out :: a -> middle -> a
139 , ft_last_outs :: a -> last -> LastOutFacts a
140 , ft_exit_out :: a -> a
143 newtype LastOutFacts a = LastOutFacts [(BlockId, a)]
144 -- ^ These are facts flowing out of a last node to the node's successors.
145 -- They are either to be set (if they pertain to the graph currently
146 -- under analysis) or propagated out of a sub-analysis
149 -- | A backward rewrite takes the same inputs as a backward transfer,
150 -- but instead of producing a fact, it produces a replacement graph or Nothing.
151 -- The type of the replacement graph is given as a type parameter 'g'
152 -- of kind * -> * -> *. This design offers great flexibility to clients,
153 -- but it might be worth simplifying this module by replacing this type
154 -- parameter with AGraph everywhere (SLPJ 19 May 2008).
156 data BackwardRewrites middle last a = BackwardRewrites
157 { br_first :: a -> BlockId -> Maybe (AGraph middle last)
158 , br_middle :: a -> middle -> Maybe (AGraph middle last)
159 , br_last :: (BlockId -> a) -> last -> Maybe (AGraph middle last)
160 , br_exit :: Maybe (AGraph middle last)
163 -- | A forward rewrite takes the same inputs as a forward transfer,
164 -- but instead of producing a fact, it produces a replacement graph or Nothing.
166 data ForwardRewrites middle last a = ForwardRewrites
167 { fr_first :: a -> BlockId -> Maybe (AGraph middle last)
168 , fr_middle :: a -> middle -> Maybe (AGraph middle last)
169 , fr_last :: a -> last -> Maybe (AGraph middle last)
170 , fr_exit :: a -> Maybe (AGraph middle last)
173 {- ===================== FIXED POINTS =================== -}
175 -- | The result of combined analysis and transformation is a
176 -- solution to the set of dataflow equations together with a 'contained value'.
177 -- This solution is a member of type class 'FixedPoint', which is parameterized by
178 -- * middle and last nodes 'm' and 'l'
179 -- * data flow fact 'fact'
180 -- * the type 'a' of the contained value
182 -- In practice, the contained value 'zdfFpContents' is either a
183 -- rewritten graph, when rewriting, or (), when solving without
184 -- rewriting. A function 'zdfFpMap' allows a client to change
185 -- the contents without changing other values.
187 -- To save space, we provide the solution 'zdfFpFacts' as a mapping
188 -- from BlockId to fact; if necessary, facts on edges can be
189 -- reconstructed using the transfer functions; this functionality is
190 -- intended to be included as the 'zdfDecoratedGraph', but the code
191 -- has not yet been implemented.
193 -- The solution may also includes a fact 'zdfFpOuputFact', which is
194 -- not associated with any label:
195 -- * for a backward problem, this is the fact at entry
196 -- * for a forward problem, this is the fact at the distinguished exit node,
197 -- if such a node is present
199 -- For a forward problem only, the solution includes 'zdfFpLastOuts',
200 -- which is the set of facts on edges leaving the graph.
202 -- The flag 'zdfGraphChanged' tells whether the engine did any rewriting.
204 class FixedPoint fp where
205 zdfFpContents :: fp m l fact a -> a
206 zdfFpFacts :: fp m l fact a -> BlockEnv fact
207 zdfFpOutputFact :: fp m l fact a -> fact -- entry for backward; exit for forward
208 zdfDecoratedGraph :: fp m l fact a -> Graph (fact, m) (fact, l)
209 zdfGraphChanged :: fp m l fact a -> ChangeFlag
210 zdfFpMap :: (a -> b) -> (fp m l fact a -> fp m l fact b)
212 -- | The class 'FixedPoint' has two instances: one for forward problems and
213 -- one for backward problems. The 'CommonFixedPoint' defines all fields
214 -- common to both. (The instance declarations are uninteresting and appear below.)
216 data CommonFixedPoint m l fact a = FP
217 { fp_facts :: BlockEnv fact
218 , fp_out :: fact -- entry for backward; exit for forward
219 , fp_changed :: ChangeFlag
220 , fp_dec_graph :: Graph (fact, m) (fact, l)
224 -- | The common fixed point is sufficient for a backward problem.
225 type BackwardFixedPoint = CommonFixedPoint
227 -- | A forward problem needs the common fields, plus the facts on the outedges.
228 data ForwardFixedPoint m l fact a = FFP
229 { ffp_common :: CommonFixedPoint m l fact a
230 , zdfFpLastOuts :: LastOutFacts fact
234 {- ============== SOLVING AND REWRITING ============== -}
236 type PassName = String
238 -- | 'zdfSolveFrom' is an overloaded name that resolves to a pure
239 -- analysis with no rewriting. It has only two instances: forward and
240 -- backward. Since it needs no rewrites, the type parameters of the
241 -- class are transfer functions and the fixed point.
244 -- An iterative solver normally starts with the bottom fact at every
245 -- node, but it can be useful in other contexts as well. For this
246 -- reason the initial set of facts (at labelled blocks only) is a
247 -- parameter to the solver.
249 -- The constraints on the type signature exist purely for debugging;
250 -- they make it possible to prettyprint nodes and facts. The parameter of
251 -- type 'PassName' is also used just for debugging.
253 -- Note that the result is a fixed point with no contents, that is,
254 -- the contents have type ().
256 -- The intent of the rest of the type signature should be obvious.
257 -- If not, place a skype call to norman-ramsey or complain bitterly
258 -- to <norman-ramsey@acm.org>.
260 class DataflowSolverDirection transfers fixedpt where
261 zdfSolveFrom :: (DebugNodes m l, Outputable a)
262 => BlockEnv a -- ^ Initial facts (unbound == bottom)
264 -> DataflowLattice a -- ^ Lattice
265 -> transfers m l a -- ^ Dataflow transfer functions
266 -> a -- ^ Fact flowing in (at entry or exit)
267 -> Graph m l -- ^ Graph to be analyzed
268 -> FuelMonad (fixedpt m l a ()) -- ^ Answers
269 zdfSolveFromL :: (DebugNodes m l, Outputable a)
270 => BlockEnv a -- Initial facts (unbound == bottom)
272 -> DataflowLattice a -- Lattice
273 -> transfers m l a -- Dataflow transfer functions
274 -> a -- Fact flowing in (at entry or exit)
275 -> LGraph m l -- Graph to be analyzed
276 -> FuelMonad (fixedpt m l a ()) -- Answers
277 zdfSolveFromL b p l t a g = zdfSolveFrom b p l t a $ quickGraph g
279 -- There are exactly two instances: forward and backward
280 instance DataflowSolverDirection ForwardTransfers ForwardFixedPoint
281 where zdfSolveFrom = solve_f
283 instance DataflowSolverDirection BackwardTransfers BackwardFixedPoint
284 where zdfSolveFrom = solve_b
287 -- | zdfRewriteFrom is an overloaded name that resolves to an
288 -- interleaved analysis and transformation. It too is instantiated in
289 -- forward and backward directions.
291 -- The type parameters of the class include not only transfer
292 -- functions and the fixed point but also rewrites and the type
293 -- constructor (here called 'graph') for making rewritten graphs. As
294 -- above, in the definitoins of the rewrites, it might simplify
295 -- matters if 'graph' were replaced with 'AGraph'.
297 -- The type signature of 'zdfRewriteFrom' is that of 'zdfSolveFrom'
298 -- with additional parameters and a different result. Of course the
299 -- rewrites are an additional parameter, but there are further
300 -- parameters which reflect the fact that rewriting consumes both
301 -- OptimizationFuel and Uniqs.
303 -- The result type is changed to reflect fuel consumption, and also
304 -- the resulting fixed point containts a rewritten graph.
306 -- John Dias is going to improve the management of Uniqs and Fuel so
307 -- that it doesn't make us sick to look at the types.
309 class DataflowSolverDirection transfers fixedpt =>
310 DataflowDirection transfers fixedpt rewrites where
311 zdfRewriteFrom :: (DebugNodes m l, Outputable a)
312 => RewritingDepth -- whether to rewrite a rewritten graph
313 -> BlockEnv a -- initial facts (unbound == botton)
318 -> a -- fact flowing in (at entry or exit)
320 -> FuelMonad (fixedpt m l a (Graph m l))
322 -- Temporarily lifting from Graph to LGraph -- an experiment to see how we
323 -- can eliminate some hysteresis between Graph and LGraph.
324 -- Perhaps Graph should be confined to dataflow code.
325 -- Trading space for time
326 quickGraph :: LastNode l => LGraph m l -> Graph m l
327 quickGraph g = Graph (ZLast $ mkBranchNode $ lg_entry g) $ lg_blocks g
329 quickLGraph :: LastNode l => Int -> Graph m l -> FuelMonad (LGraph m l)
330 quickLGraph args (Graph (ZLast (LastOther l)) blockenv)
331 | isBranchNode l = return $ LGraph (branchNodeTarget l) args blockenv
332 quickLGraph args g = F.lGraphOfGraph g args
334 fixptWithLGraph :: LastNode l => Int -> CommonFixedPoint m l fact (Graph m l) ->
335 FuelMonad (CommonFixedPoint m l fact (LGraph m l))
336 fixptWithLGraph args cfp =
337 do fp_c <- quickLGraph args $ fp_contents cfp
338 return $ cfp {fp_contents = fp_c}
340 ffixptWithLGraph :: LastNode l => Int -> ForwardFixedPoint m l fact (Graph m l) ->
341 FuelMonad (ForwardFixedPoint m l fact (LGraph m l))
342 ffixptWithLGraph args fp =
343 do common <- fixptWithLGraph args $ ffp_common fp
344 return $ fp {ffp_common = common}
346 zdfFRewriteFromL :: (DebugNodes m l, Outputable a)
347 => RewritingDepth -- whether to rewrite a rewritten graph
348 -> BlockEnv a -- initial facts (unbound == botton)
351 -> ForwardTransfers m l a
352 -> ForwardRewrites m l a
353 -> a -- fact flowing in (at entry or exit)
355 -> FuelMonad (ForwardFixedPoint m l a (LGraph m l))
356 zdfFRewriteFromL d b p l t r a g@(LGraph _ args _) =
357 do fp <- zdfRewriteFrom d b p l t r a $ quickGraph g
358 ffixptWithLGraph args fp
360 zdfBRewriteFromL :: (DebugNodes m l, Outputable a)
361 => RewritingDepth -- whether to rewrite a rewritten graph
362 -> BlockEnv a -- initial facts (unbound == botton)
365 -> BackwardTransfers m l a
366 -> BackwardRewrites m l a
367 -> a -- fact flowing in (at entry or exit)
369 -> FuelMonad (BackwardFixedPoint m l a (LGraph m l))
370 zdfBRewriteFromL d b p l t r a g@(LGraph _ args _) =
371 do fp <- zdfRewriteFrom d b p l t r a $ quickGraph g
372 fixptWithLGraph args fp
375 data RewritingDepth = RewriteShallow | RewriteDeep
376 -- When a transformation proposes to rewrite a node,
377 -- you can either ask the system to
378 -- * "shallow": accept the new graph, analyse it without further rewriting
379 -- * "deep": recursively analyse-and-rewrite the new graph
382 -- There are currently four instances, but there could be more
383 -- forward, backward (instantiates transfers, fixedpt, rewrites)
384 -- Graph, AGraph (instantiates graph)
386 instance DataflowDirection ForwardTransfers ForwardFixedPoint ForwardRewrites
387 where zdfRewriteFrom = rewrite_f_agraph
389 instance DataflowDirection BackwardTransfers BackwardFixedPoint BackwardRewrites
390 where zdfRewriteFrom = rewrite_b_agraph
393 {- =================== IMPLEMENTATIONS ===================== -}
396 -----------------------------------------------------------
397 -- solve_f: forward, pure
399 solve_f :: (DebugNodes m l, Outputable a)
400 => BlockEnv a -- initial facts (unbound == bottom)
402 -> DataflowLattice a -- lattice
403 -> ForwardTransfers m l a -- dataflow transfer functions
405 -> Graph m l -- graph to be analyzed
406 -> FuelMonad (ForwardFixedPoint m l a ()) -- answers
407 solve_f env name lattice transfers in_fact g =
408 runDFM lattice $ fwd_pure_anal name env transfers in_fact g
410 rewrite_f_agraph :: (DebugNodes m l, Outputable a)
415 -> ForwardTransfers m l a
416 -> ForwardRewrites m l a
417 -> a -- fact flowing in (at entry or exit)
419 -> FuelMonad (ForwardFixedPoint m l a (Graph m l))
420 rewrite_f_agraph depth start_facts name lattice transfers rewrites in_fact g =
422 do fuel <- fuelRemaining
423 (fp, fuel') <- forward_rew maybeRewriteWithFuel depth start_facts name
424 transfers rewrites in_fact g fuel
425 fuelDecrement name fuel fuel'
428 areturn :: AGraph m l -> DFM a (Graph m l)
429 areturn g = liftToDFM $ liftUniq $ graphOfAGraph g
431 -- | Here we prefer not simply to slap on 'goto eid' because this
432 -- introduces an unnecessary basic block at each rewrite, and we don't
433 -- want to stress out the finite map more than necessary
434 lgraphToGraph :: LastNode l => LGraph m l -> Graph m l
435 lgraphToGraph (LGraph eid _ blocks) =
436 if flip any (eltsUFM blocks) $ \block -> any (== eid) (succs block) then
437 Graph (ZLast (mkBranchNode eid)) blocks
438 else -- common case: entry is not a branch target
439 let Block _ _ entry = lookupBlockEnv blocks eid `orElse` panic "missing entry!"
440 in Graph entry (delFromUFM blocks eid)
443 class (Outputable m, Outputable l, LastNode l, Outputable (LGraph m l)) => DebugNodes m l
445 fwd_pure_anal :: (DebugNodes m l, LastNode l, Outputable a)
448 -> ForwardTransfers m l a
451 -> DFM a (ForwardFixedPoint m l a ())
453 fwd_pure_anal name env transfers in_fact g =
454 do (fp, _) <- anal_f name env transfers panic_rewrites in_fact g panic_fuel
456 where -- definitiely a case of "I love lazy evaluation"
457 anal_f = forward_sol (\_ _ -> Nothing) panic_depth
458 panic_rewrites = panic "pure analysis asked for a rewrite function"
459 panic_fuel = panic "pure analysis asked for fuel"
460 panic_depth = panic "pure analysis asked for a rewrite depth"
462 -----------------------------------------------------------------------
464 -- Here beginneth the super-general functions
466 -- Think of them as (typechecked) macros
467 -- * They are not exported
469 -- * They are called by the specialised wrappers
470 -- above, and always inlined into their callers
472 -- There are four functions, one for each combination of:
476 -- A "solver" produces a (DFM f (f, Fuel)),
477 -- where f is the fact at entry(Bwd)/exit(Fwd)
478 -- and from the DFM you can extract
483 -- A "rewriter" produces a rewritten *Graph* as well
485 -- Both constrain their rewrites by
487 -- b) RewritingDepth: shallow/deep
489 -----------------------------------------------------------------------
491 type Fuel = OptimizationFuel
493 {-# INLINE forward_sol #-}
496 (DebugNodes m l, LastNode l, Outputable a)
497 => (forall a . Fuel -> Maybe a -> Maybe a)
498 -- Squashes proposed rewrites if there is
499 -- no more fuel; OR if we are doing a pure
500 -- analysis, so totally ignore the rewrite
501 -- ie. For pure-analysis the fn is (\_ _ -> Nothing)
502 -> RewritingDepth -- Shallow/deep
504 -> BlockEnv a -- Initial set of facts
505 -> ForwardTransfers m l a
506 -> ForwardRewrites m l a
510 -> DFM a (ForwardFixedPoint m l a (), Fuel)
511 forward_sol check_maybe = forw
513 forw :: RewritingDepth
516 -> ForwardTransfers m l a
517 -> ForwardRewrites m l a
521 -> DFM a (ForwardFixedPoint m l a (), Fuel)
522 forw rewrite name start_facts transfers rewrites =
523 let anal_f :: DFM a b -> a -> Graph m l -> DFM a b
524 anal_f finish in' g =
525 do { fwd_pure_anal name emptyBlockEnv transfers in' g; finish }
527 solve :: DFM a b -> a -> Graph m l -> Fuel -> DFM a (b, Fuel)
528 solve finish in_fact (Graph entry blockenv) fuel =
529 let blocks = G.postorder_dfs_from blockenv entry
530 set_or_save = mk_set_or_save (isJust . lookupBlockEnv blockenv)
531 set_successor_facts (Block id _ tail) fuel =
532 do { idfact <- getFact id
533 ; (last_outs, fuel) <-
534 case check_maybe fuel $ fr_first rewrites idfact id of
535 Nothing -> solve_tail (ft_first_out transfers idfact id) tail fuel
538 (a, fuel) <- subAnalysis' $
540 RewriteDeep -> solve getExitFact idfact g (oneLessFuel fuel)
542 do { a <- anal_f getExitFact idfact g
543 ; return (a, oneLessFuel fuel) }
544 solve_tail a tail fuel
545 ; set_or_save last_outs
548 in do { (last_outs, fuel) <- solve_tail in_fact entry fuel
549 ; set_or_save last_outs
550 ; fuel <- run "forward" name set_successor_facts blocks fuel
555 solve_tail in' (G.ZTail m t) fuel =
556 case check_maybe fuel $ fr_middle rewrites in' m of
557 Nothing -> solve_tail (ft_middle_out transfers in' m) t fuel
560 ; (a, fuel) <- subAnalysis' $
562 RewriteDeep -> solve getExitFact in' g (oneLessFuel fuel)
563 RewriteShallow -> do { a <- anal_f getExitFact in' g
564 ; return (a, oneLessFuel fuel) }
565 ; solve_tail a t fuel
567 solve_tail in' (G.ZLast l) fuel =
568 case check_maybe fuel $ either_last rewrites in' l of
570 case l of LastOther l -> return (ft_last_outs transfers in' l, fuel)
571 LastExit -> do { setExitFact (ft_exit_out transfers in')
572 ; return (LastOutFacts [], fuel) }
575 ; (last_outs :: LastOutFacts a, fuel) <- subAnalysis' $
577 RewriteDeep -> solve lastOutFacts in' g (oneLessFuel fuel)
578 RewriteShallow -> do { los <- anal_f lastOutFacts in' g
579 ; return (los, fuel) }
580 ; return (last_outs, fuel)
583 fixed_point in_fact g fuel =
584 do { setAllFacts start_facts
585 ; (a, fuel) <- solve getExitFact in_fact g fuel
586 ; facts <- getAllFacts
587 ; last_outs <- lastOutFacts
588 ; let cfp = FP facts a NoChange (panic "no decoration?!") ()
589 ; let fp = FFP cfp last_outs
593 either_last rewrites in' (LastExit) = fr_exit rewrites in'
594 either_last rewrites in' (LastOther l) = fr_last rewrites in' l
601 mk_set_or_save :: (DataflowAnalysis df, Monad (df a), Outputable a) =>
602 (BlockId -> Bool) -> LastOutFacts a -> df a ()
603 mk_set_or_save is_local (LastOutFacts l) = mapM_ set_or_save_one l
604 where set_or_save_one (id, a) =
605 if is_local id then setFact id a else addLastOutFact (id, a)
610 {-# INLINE forward_rew #-}
613 (DebugNodes m l, LastNode l, Outputable a)
614 => (forall a . Fuel -> Maybe a -> Maybe a)
618 -> ForwardTransfers m l a
619 -> ForwardRewrites m l a
623 -> DFM a (ForwardFixedPoint m l a (Graph m l), Fuel)
624 forward_rew check_maybe = forw
626 solve = forward_sol check_maybe
627 forw :: RewritingDepth
630 -> ForwardTransfers m l a
631 -> ForwardRewrites m l a
635 -> DFM a (ForwardFixedPoint m l a (Graph m l), Fuel)
636 forw depth xstart_facts name transfers rewrites in_factx gx fuelx =
637 let rewrite :: BlockEnv a -> DFM a b
638 -> a -> Graph m l -> Fuel
639 -> DFM a (b, Graph m l, Fuel)
640 rewrite start finish in_fact g fuel =
641 let Graph entry blockenv = g
642 blocks = G.postorder_dfs_from blockenv entry
643 in do { solve depth name start transfers rewrites in_fact g fuel
644 ; eid <- freshBlockId "temporary entry id"
645 ; (rewritten, fuel) <-
646 rew_tail (ZFirst eid Nothing) in_fact entry emptyBlockEnv fuel
647 ; (rewritten, fuel) <- rewrite_blocks blocks rewritten fuel
649 ; return (a, lgraphToGraph (LGraph eid 0 rewritten), fuel)
651 don't_rewrite facts finish in_fact g fuel =
652 do { solve depth name facts transfers rewrites in_fact g fuel
654 ; return (a, g, fuel)
656 inner_rew :: DFM a f -> a -> Graph m l -> Fuel -> DFM a (f, Graph m l, Fuel)
657 inner_rew f i g fu = getAllFacts >>= \facts -> inner_rew' facts f i g fu
658 where inner_rew' = case depth of RewriteShallow -> don't_rewrite
659 RewriteDeep -> rewrite
661 do { (a, g, fuel) <- rewrite xstart_facts getExitFact in_factx gx fuelx
662 ; facts <- getAllFacts
663 ; changed <- graphWasRewritten
664 ; last_outs <- lastOutFacts
665 ; let cfp = FP facts a changed (panic "no decoration?!") g
666 ; let fp = FFP cfp last_outs
669 rewrite_blocks :: [Block m l] -> (BlockEnv (Block m l))
670 -> Fuel -> DFM a (BlockEnv (Block m l), Fuel)
671 rewrite_blocks [] rewritten fuel = return (rewritten, fuel)
672 rewrite_blocks (G.Block id off t : bs) rewritten fuel =
673 do let h = ZFirst id off
675 case check_maybe fuel $ fr_first rewrites a id of
676 Nothing -> do { (rewritten, fuel) <-
677 rew_tail h (ft_first_out transfers a id)
679 ; rewrite_blocks bs rewritten fuel }
680 Just g -> do { markGraphRewritten
682 ; (outfact, g, fuel) <- inner_rew getExitFact a g fuel
683 ; let (blocks, h) = splice_head' h g
684 ; (rewritten, fuel) <-
685 rew_tail h outfact t (blocks `plusUFM` rewritten) fuel
686 ; rewrite_blocks bs rewritten fuel }
688 rew_tail head in' (G.ZTail m t) rewritten fuel =
689 my_trace "Rewriting middle node" (ppr m) $
690 case check_maybe fuel $ fr_middle rewrites in' m of
691 Nothing -> rew_tail (G.ZHead head m) (ft_middle_out transfers in' m) t
693 Just g -> do { markGraphRewritten
695 ; (a, g, fuel) <- inner_rew getExitFact in' g fuel
696 ; let (blocks, h) = G.splice_head' head g
697 ; rew_tail h a t (blocks `plusUFM` rewritten) fuel
699 rew_tail h in' (G.ZLast l) rewritten fuel =
700 my_trace "Rewriting last node" (ppr l) $
701 case check_maybe fuel $ either_last rewrites in' l of
702 Nothing -> do check_facts in' l
703 return (insertBlock (zipht h (G.ZLast l)) rewritten, fuel)
704 Just g -> do { markGraphRewritten
706 ; ((), g, fuel) <- inner_rew (return ()) in' g fuel
707 ; let g' = G.splice_head_only' h g
708 ; return (G.lg_blocks g' `plusUFM` rewritten, fuel)
710 either_last rewrites in' (LastExit) = fr_exit rewrites in'
711 either_last rewrites in' (LastOther l) = fr_last rewrites in' l
712 check_facts in' (LastOther l) =
713 let LastOutFacts last_outs = ft_last_outs transfers in' l
714 in mapM (uncurry checkFactMatch) last_outs
715 check_facts _ LastExit = return []
718 lastOutFacts :: DFM f (LastOutFacts f)
719 lastOutFacts = bareLastOutFacts >>= return . LastOutFacts
721 {- ================================================================ -}
723 solve_b :: (DebugNodes m l, Outputable a)
724 => BlockEnv a -- initial facts (unbound == bottom)
726 -> DataflowLattice a -- lattice
727 -> BackwardTransfers m l a -- dataflow transfer functions
729 -> Graph m l -- graph to be analyzed
730 -> FuelMonad (BackwardFixedPoint m l a ()) -- answers
731 solve_b env name lattice transfers exit_fact g =
732 runDFM lattice $ bwd_pure_anal name env transfers g exit_fact
735 rewrite_b_agraph :: (DebugNodes m l, Outputable a)
740 -> BackwardTransfers m l a
741 -> BackwardRewrites m l a
742 -> a -- fact flowing in at exit
744 -> FuelMonad (BackwardFixedPoint m l a (Graph m l))
745 rewrite_b_agraph depth start_facts name lattice transfers rewrites exit_fact g =
747 do fuel <- fuelRemaining
748 (fp, fuel') <- backward_rew maybeRewriteWithFuel depth start_facts name
749 transfers rewrites g exit_fact fuel
750 fuelDecrement name fuel fuel'
755 {-# INLINE backward_sol #-}
758 (DebugNodes m l, LastNode l, Outputable a)
759 => (forall a . Fuel -> Maybe a -> Maybe a)
763 -> BackwardTransfers m l a
764 -> BackwardRewrites m l a
768 -> DFM a (BackwardFixedPoint m l a (), Fuel)
769 backward_sol check_maybe = back
771 back :: RewritingDepth
774 -> BackwardTransfers m l a
775 -> BackwardRewrites m l a
779 -> DFM a (BackwardFixedPoint m l a (), Fuel)
780 back rewrite name start_facts transfers rewrites =
781 let anal_b :: Graph m l -> a -> DFM a a
783 do { fp <- bwd_pure_anal name emptyBlockEnv transfers g out
784 ; return $ zdfFpOutputFact fp }
786 subsolve :: AGraph m l -> a -> Fuel -> DFM a (a, Fuel)
789 RewriteDeep -> \g a fuel ->
790 subAnalysis' $ do { g <- areturn g; solve g a (oneLessFuel fuel) }
791 RewriteShallow -> \g a fuel ->
792 subAnalysis' $ do { g <- areturn g; a <- anal_b g a
793 ; return (a, oneLessFuel fuel) }
795 solve :: Graph m l -> a -> Fuel -> DFM a (a, Fuel)
796 solve (Graph entry blockenv) exit_fact fuel =
797 let blocks = reverse $ G.postorder_dfs_from blockenv entry
798 last_in _env (LastExit) = exit_fact
799 last_in env (LastOther l) = bt_last_in transfers env l
800 last_rew _env (LastExit) = br_exit rewrites
801 last_rew env (LastOther l) = br_last rewrites env l
802 set_block_fact block fuel =
803 let (h, l) = G.goto_end (G.unzip block) in
806 case check_maybe fuel $ last_rew env l of
807 Nothing -> return (last_in env l, fuel)
808 Just g -> subsolve g exit_fact fuel
809 ; set_head_fact h a fuel
812 in do { fuel <- run "backward" name set_block_fact blocks fuel
813 ; eid <- freshBlockId "temporary entry id"
814 ; fuel <- set_block_fact (Block eid Nothing entry) fuel
820 set_head_fact (G.ZFirst id _) a fuel =
821 case check_maybe fuel $ br_first rewrites a id of
822 Nothing -> do { my_trace "set_head_fact" (ppr id <+> text "=" <+>
823 ppr (bt_first_in transfers a id)) $
824 setFact id $ bt_first_in transfers a id
826 Just g -> do { (a, fuel) <- subsolve g a fuel
830 set_head_fact (G.ZHead h m) a fuel =
831 case check_maybe fuel $ br_middle rewrites a m of
832 Nothing -> set_head_fact h (bt_middle_in transfers a m) fuel
833 Just g -> do { (a, fuel) <- subsolve g a fuel
834 ; set_head_fact h a fuel }
836 fixed_point g exit_fact fuel =
837 do { setAllFacts start_facts
838 ; (a, fuel) <- solve g exit_fact fuel
839 ; facts <- getAllFacts
840 ; let cfp = FP facts a NoChange (panic "no decoration?!") ()
845 bwd_pure_anal :: (DebugNodes m l, LastNode l, Outputable a)
848 -> BackwardTransfers m l a
851 -> DFM a (BackwardFixedPoint m l a ())
853 bwd_pure_anal name env transfers g exit_fact =
854 do (fp, _) <- anal_b name env transfers panic_rewrites g exit_fact panic_fuel
856 where -- another case of "I love lazy evaluation"
857 anal_b = backward_sol (\_ _ -> Nothing) panic_depth
858 panic_rewrites = panic "pure analysis asked for a rewrite function"
859 panic_fuel = panic "pure analysis asked for fuel"
860 panic_depth = panic "pure analysis asked for a rewrite depth"
863 {- ================================================================ -}
865 {-# INLINE backward_rew #-}
868 (DebugNodes m l, LastNode l, Outputable a)
869 => (forall a . Fuel -> Maybe a -> Maybe a)
873 -> BackwardTransfers m l a
874 -> BackwardRewrites m l a
878 -> DFM a (BackwardFixedPoint m l a (Graph m l), Fuel)
879 backward_rew check_maybe = back
881 solve = backward_sol check_maybe
882 back :: RewritingDepth
885 -> BackwardTransfers m l a
886 -> BackwardRewrites m l a
890 -> DFM a (BackwardFixedPoint m l a (Graph m l), Fuel)
891 back depth xstart_facts name transfers rewrites gx exit_fact fuelx =
892 let rewrite :: BlockEnv a
893 -> Graph m l -> a -> Fuel
894 -> DFM a (a, Graph m l, Fuel)
895 rewrite start g exit_fact fuel =
896 let Graph entry blockenv = g
897 blocks = reverse $ G.postorder_dfs_from blockenv entry
898 in do { (FP env in_fact _ _ _, _) <- -- don't drop the entry fact!
899 solve depth name start transfers rewrites g exit_fact fuel
900 --; env <- getAllFacts
901 ; my_trace "facts after solving" (ppr env) $ return ()
902 ; eid <- freshBlockId "temporary entry id"
903 ; (rewritten, fuel) <- rewrite_blocks True blocks emptyBlockEnv fuel
904 -- We can't have the fact check fail on the bogus entry, which _may_ change
905 ; (rewritten, fuel) <- rewrite_blocks False [Block eid Nothing entry] rewritten fuel
906 ; my_trace "eid" (ppr eid) $ return ()
907 ; my_trace "exit_fact" (ppr exit_fact) $ return ()
908 ; my_trace "in_fact" (ppr in_fact) $ return ()
909 ; return (in_fact, lgraphToGraph (LGraph eid 0 rewritten), fuel)
910 } -- Remember: the entry fact computed by @solve@ accounts for rewriting
911 don't_rewrite facts g exit_fact fuel =
913 solve depth name facts transfers rewrites g exit_fact fuel
914 ; return (zdfFpOutputFact fp, g, fuel) }
915 inner_rew :: Graph m l -> a -> Fuel -> DFM a (a, Graph m l, Fuel)
916 inner_rew g a f = getAllFacts >>= \facts -> inner_rew' facts g a f
917 where inner_rew' = case depth of RewriteShallow -> don't_rewrite
918 RewriteDeep -> rewrite
920 do { (a, g, fuel) <- rewrite xstart_facts gx exit_fact fuelx
921 ; facts <- getAllFacts
922 ; changed <- graphWasRewritten
923 ; let fp = FP facts a changed (panic "no decoration?!") g
926 rewrite_blocks :: Bool -> [Block m l] -> (BlockEnv (Block m l))
927 -> Fuel -> DFM a (BlockEnv (Block m l), Fuel)
928 rewrite_blocks check bs rewritten fuel =
930 ; let rew [] r f = return (r, f)
932 do { (r, f) <- rewrite_block check env b r f; rew bs r f }
933 ; rew bs rewritten fuel }
934 rewrite_block check env b rewritten fuel =
935 let (h, l) = G.goto_end (G.unzip b) in
936 case maybeRewriteWithFuel fuel $ either_last env l of
937 Nothing -> propagate check fuel h (last_in env l) (ZLast l) rewritten
939 do { markGraphRewritten
941 ; (a, g, fuel) <- inner_rew g exit_fact fuel
942 ; let G.Graph t new_blocks = g
943 ; let rewritten' = new_blocks `plusUFM` rewritten
944 ; propagate check fuel h a t rewritten' -- continue at entry of g
946 either_last _env (LastExit) = br_exit rewrites
947 either_last env (LastOther l) = br_last rewrites env l
948 last_in _env (LastExit) = exit_fact
949 last_in env (LastOther l) = bt_last_in transfers env l
950 propagate check fuel (ZHead h m) a tail rewritten =
951 case maybeRewriteWithFuel fuel $ br_middle rewrites a m of
953 propagate check fuel h (bt_middle_in transfers a m) (ZTail m tail) rewritten
955 do { markGraphRewritten
957 ; my_trace "With Facts" (ppr a) $ return ()
958 ; my_trace " Rewrote middle node"
959 (f4sep [ppr m, text "to", pprGraph g]) $
961 ; (a, g, fuel) <- inner_rew g a fuel
962 ; let Graph t newblocks = G.splice_tail g tail
963 ; my_trace "propagating facts" (ppr a) $
964 propagate check fuel h a t (newblocks `plusUFM` rewritten) }
965 propagate check fuel (ZFirst id off) a tail rewritten =
966 case maybeRewriteWithFuel fuel $ br_first rewrites a id of
967 Nothing -> do { if check then checkFactMatch id $ bt_first_in transfers a id
969 ; return (insertBlock (Block id off tail) rewritten, fuel) }
971 do { markGraphRewritten
973 ; my_trace "Rewrote first node"
974 (f4sep [ppr id <> colon, text "to", pprGraph g]) $ return ()
975 ; (a, g, fuel) <- inner_rew g a fuel
976 ; if check then checkFactMatch id a else return ()
977 ; let Graph t newblocks = G.splice_tail g tail
978 ; let r = insertBlock (Block id off t) (newblocks `plusUFM` rewritten)
982 {- ================================================================ -}
984 instance FixedPoint CommonFixedPoint where
985 zdfFpFacts = fp_facts
986 zdfFpOutputFact = fp_out
987 zdfGraphChanged = fp_changed
988 zdfDecoratedGraph = fp_dec_graph
989 zdfFpContents = fp_contents
990 zdfFpMap f (FP fs out ch dg a) = FP fs out ch dg (f a)
992 instance FixedPoint ForwardFixedPoint where
993 zdfFpFacts = fp_facts . ffp_common
994 zdfFpOutputFact = fp_out . ffp_common
995 zdfGraphChanged = fp_changed . ffp_common
996 zdfDecoratedGraph = fp_dec_graph . ffp_common
997 zdfFpContents = fp_contents . ffp_common
998 zdfFpMap f (FFP fp los) = FFP (zdfFpMap f fp) los
1004 my_trace :: String -> SDoc -> a -> a
1005 my_trace = if dump_things then pprTrace else \_ _ a -> a
1008 -- | Here's a function to run an action on blocks until we reach a fixed point.
1009 run :: (Outputable a, DebugNodes m l) =>
1010 String -> String -> (Block m l -> b -> DFM a b) -> [Block m l] -> b -> DFM a b
1011 run dir name do_block blocks b =
1012 do { show_blocks $ iterate (1::Int) }
1014 -- N.B. Each iteration starts with the same transaction limit;
1015 -- only the rewrites in the final iteration actually count
1016 trace_block b block =
1017 my_trace "about to do" (text name <+> text "on" <+> ppr (blockId block)) $
1020 do { markFactsUnchanged
1021 ; b <- foldM trace_block b blocks
1022 ; changed <- factsStatus
1023 ; facts <- getAllFacts
1024 ; let depth = 0 -- was nesting depth
1027 NoChange -> unchanged depth $ return b
1029 pprFacts depth n facts $
1030 if n < 1000 then iterate (n+1)
1033 msg n = concat [name, " didn't converge in ", show n, " " , dir,
1035 my_nest depth sdoc = my_trace "" $ nest (3*depth) sdoc
1036 ppIter depth n = my_nest depth (empty $$ text "*************** iteration" <+> pp_i n)
1037 pp_i n = int n <+> text "of" <+> text name <+> text "on" <+> graphId
1039 my_nest depth (text "facts for" <+> graphId <+> text "are unchanged")
1041 graphId = case blocks of { Block id _ _ : _ -> ppr id ; [] -> text "<empty>" }
1042 show_blocks = my_trace "Blocks:" (vcat (map pprBlock blocks))
1043 pprBlock (Block id off t) = nest 2 (pprFact' (id, off, t))
1044 pprFacts depth n env =
1045 my_nest depth (text "facts for iteration" <+> pp_i n <+> text "are:" $$
1046 (nest 2 $ vcat $ map pprFact $ ufmToList env))
1047 pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)
1048 pprFact' (id, off, a) = hang (ppr id <> parens (ppr off) <> colon) 4 (ppr a)
1051 f4sep :: [SDoc] -> SDoc
1053 f4sep (d:ds) = fsep (d : map (nest 4) ds)
1056 subAnalysis' :: (Monad (m f), DataflowAnalysis m, Outputable f) =>
1059 do { a <- subAnalysis $
1060 do { a <- m; facts <- getAllFacts
1061 ; my_trace "after sub-analysis facts are" (pprFacts facts) $
1063 ; facts <- getAllFacts
1064 ; my_trace "in parent analysis facts are" (pprFacts facts) $
1066 where pprFacts env = nest 2 $ vcat $ map pprFact $ ufmToList env
1067 pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)