-{-# OPTIONS -Wall -fno-warn-name-shadowing #-}
-{-# LANGUAGE ScopedTypeVariables, MultiParamTypeClasses #-}
+{-# LANGUAGE MultiParamTypeClasses, ScopedTypeVariables #-}
+{-# OPTIONS -fglasgow-exts #-}
+-- -fglagow-exts for kind signatures
+
module ZipDataflow
- ( Answer(..)
- , BComputation(..), BAnalysis, BTransformation, BFunctionalTransformation
- , BPass, BUnlimitedPass
- , FComputation(..), FAnalysis, FTransformation, FPass, FUnlimitedPass
- , LastOutFacts(..)
- , DebugNodes
- , anal_b, a_t_b, a_ft_b, a_ft_b_unlimited, ignore_transactions_b
- , anal_f, a_t_f
- , run_b_anal, run_f_anal
- , refine_f_anal, refine_b_anal, fold_edge_facts_b, fold_edge_facts_with_nodes_b
- , b_rewrite, f_rewrite
- , solve_graph_b, solve_graph_f
- )
+ ( DebugNodes(), RewritingDepth(..), LastOutFacts(..)
+ , zdfSolveFrom, zdfRewriteFrom
+ , zdfSolveFromL
+ , ForwardTransfers(..), BackwardTransfers(..)
+ , ForwardRewrites(..), BackwardRewrites(..)
+ , ForwardFixedPoint, BackwardFixedPoint
+ , zdfFpFacts
+ , zdfFpOutputFact
+ , zdfGraphChanged
+ , zdfDecoratedGraph -- not yet implemented
+ , zdfFpContents
+ , zdfFpLastOuts
+ , zdfBRewriteFromL, zdfFRewriteFromL
+ )
where
+import BlockId
import CmmTx
import DFMonad
-import ZipCfg hiding (freshBlockId) -- use version from DFMonad
+import OptimizationFuel as F
+import MkZipCfg
+import ZipCfg
import qualified ZipCfg as G
+import Maybes
import Outputable
-import Panic
-import UniqFM
-import UniqSupply
import Control.Monad
-import Maybe
-
-{-
-
-\section{A very polymorphic infrastructure for dataflow problems}
-
-This module presents a framework for solving iterative dataflow
-problems.
-There are two major submodules: one for forward problems and another
-for backward problems.
-Both modules incorporate the composition framework developed by
-Lerner, Grove, and Chambers.
-They also support a \emph{transaction limit}, which enables the
-binary-search debugging technique developed by Whalley and Davidson
-under the name \emph{vpoiso}.
-Transactions may either be known to the individual dataflow solvers or
-may be managed by the framework.
--}
-
--- | In the composition framework, a pass either produces a dataflow
--- fact or proposes to rewrite the graph. To make life easy for the
--- clients, the rewrite is given in unlabelled form, but we use
--- labelled form internally throughout, because it greatly simplifies
--- the implementation not to have the first block be a special case
--- edverywhere.
-
-data Answer m l a = Dataflow a | Rewrite (Graph m l)
-
-
-{-
-
-\subsection {Descriptions of dataflow passes}
-
-\paragraph{Passes for backward dataflow problems}
-
-The computation of a fact is the basis of a dataflow pass.
-A~computation takes not one but two type parameters:
-\begin{itemize}
-\item
-Type parameter [['i]] is an input, from which it should be possible to
-derived a dataflow fact of interest.
-For example, [['i]] might be equal to a fact, or it might be a tuple
-of which one element is a fact.
-\item
-Type parameter [['o]] is an output, or possibly a function from
-[[txlimit]] to an output
-\end{itemize}
-Backward analyses compute [[in]] facts (facts on inedges).
-<<exported types for backward analyses>>=
-
--}
-
-data BComputation middle last input output = BComp
- { bc_name :: String
- , bc_exit_in :: output
- , bc_last_in :: (BlockId -> input) -> last -> output
- , bc_middle_in :: input -> middle -> output
- , bc_first_in :: input -> BlockId -> output
- }
-
--- | From these elements we build several kinds of passes:
--- * A pure analysis computes a fact, using that fact as input and output.
--- * A pure transformation computes no facts but only changes the graph.
--- * A fully general pass both computes a fact and rewrites the graph,
--- respecting the current transaction limit.
-
-type BAnalysis m l a = BComputation m l a a
-type BTransformation m l a = BComputation m l a (Maybe (UniqSM (Graph m l)))
-type BFunctionalTransformation m l a = BComputation m l a (Maybe (Graph m l))
-
-type BPass m l a = BComputation m l a (Txlimit -> DFM a (Answer m l a))
-type BUnlimitedPass m l a = BComputation m l a ( DFM a (Answer m l a))
-
-{-
-\paragraph{Passes for forward dataflow problems}
-
-A forward dataflow pass has a similar structure, but the details are
-different. In particular, the output fact from a [[last]] node has a
-higher-order representation: it takes a function that mutates a
-[[uid]] to account for the new fact, then performs the necessary
-mutation on every successor of the last node. We therefore have two
-kinds of type parameter for outputs: output from a [[middle]] node
-is~[[outmid]], and output from a [[last]] node is~[[outlast]].
--}
-
-data FComputation middle last input outmid outlast = FComp
- { fc_name :: String
- , fc_first_out :: input -> BlockId -> outmid
- , fc_middle_out :: input -> middle -> outmid
- , fc_last_outs :: input -> last -> outlast
- , fc_exit_outs :: input -> outlast
- }
-
--- | The notions of analysis, pass, and transformation are analogous to the
--- backward case.
+
+{-
+
+This module implements two useful tools:
+
+ 1. An iterative solver for dataflow problems
+
+ 2. The combined dataflow-analysis-and-transformation framework
+ described by Lerner, Grove, and Chambers in their excellent
+ 2002 POPL paper (http://tinyurl.com/3zycbr or
+ http://tinyurl.com/3pnscd).
+
+Each tool comes in two flavors: one for forward dataflow problems
+and one for backward dataflow problems.
+
+We quote the paper above:
+
+ Dataflow analyses can have mutually beneficial interactions.
+ Previous efforts to exploit these interactions have either
+ (1) iteratively performed each individual analysis until no
+ further improvements are discovered or (2) developed "super-
+ analyses" that manually combine conceptually separate anal-
+ yses. We have devised a new approach that allows anal-
+ yses to be defined independently while still enabling them
+ to be combined automatically and profitably. Our approach
+ avoids the loss of precision associated with iterating indi-
+ vidual analyses and the implementation difficulties of man-
+ ually writing a super-analysis.
+
+The key idea is to provide at each CFG node not only a dataflow
+transfer function but also a rewriting function that has the option to
+replace the node with a new (possibly empty) graph. The rewriting
+function takes a dataflow fact as input, and the fact is used to
+justify any rewriting. For example, in a backward problem, the fact
+that variable x is dead can be used to justify rewriting node
+ x := e
+to the empty graph. In a forward problem, the fact that x == 7 can
+be used to justify rewriting node
+ y := x + 1
+to
+ y := 8
+which in turn will be analyzed and produce a new fact:
+x == 7 and y == 8.
+
+In its most general form, this module takes as input graph, transfer
+equations, rewrites, and an initial set of dataflow facts, and
+iteratively computes a new graph and a new set of dataflow facts such
+that
+ * The set of facts is a fixed point of the transfer equations
+ * The graph has been rewritten as much as is consistent with
+ the given facts and requested rewriting depth (see below)
+N.B. 'A set of facts' is shorthand for 'A finite map from CFG label to fact'.
+
+The types of transfer equations, rewrites, and fixed points are
+different for forward and backward problems. To avoid cluttering the
+name space with two versions of every name, other names such as
+zdfSolveFrom are overloaded to work in both forward or backward
+directions. This design decision is based on experience with the
+predecessor module, which has been mercifully deleted.
+
+
+This module is deliberately very abstract. It is a completely general
+framework and well-nigh impossible to understand in isolation. The
+cautious reader will begin with some concrete examples in the form of
+clients. NR recommends
+
+ CmmLiveZ A simple liveness analysis
+
+ CmmSpillReload.removeDeadAssignmentsAndReloads
+ A piece of spaghetti to pull on, which leads to
+ - a two-part liveness analysis that tracks
+ variables live in registers and live on the stack
+ - elimination of assignments to dead variables
+ - elimination of redundant reloads
+
+Even hearty souls should avoid the CmmProcPointZ client, at least for
+the time being.
+
+-}
+
+
+{- ============ TRANSFER FUNCTIONS AND REWRITES =========== -}
+
+-- | For a backward transfer, you're given the fact on a node's
+-- outedge and you compute the fact on the inedge. Facts have type 'a'.
+-- A last node may have multiple outedges, each pointing to a labelled
+-- block, so instead of a fact it is given a mapping from BlockId to fact.
+
+data BackwardTransfers middle last a = BackwardTransfers
+ { bt_first_in :: BlockId -> a -> a
+ , bt_middle_in :: middle -> a -> a
+ , bt_last_in :: last -> (BlockId -> a) -> a
+ }
+
+-- | For a forward transfer, you're given the fact on a node's
+-- inedge and you compute the fact on the outedge. Because a last node
+-- may have multiple outedges, each pointing to a labelled
+-- block, so instead of a fact it produces a list of (BlockId, fact) pairs.
+
+data ForwardTransfers middle last a = ForwardTransfers
+ { ft_first_out :: BlockId -> a -> a
+ , ft_middle_out :: middle -> a -> a
+ , ft_last_outs :: last -> a -> LastOutFacts a
+ , ft_exit_out :: a -> a
+ }
newtype LastOutFacts a = LastOutFacts [(BlockId, a)]
-- ^ These are facts flowing out of a last node to the node's successors.
-- They are either to be set (if they pertain to the graph currently
-- under analysis) or propagated out of a sub-analysis
-type FAnalysis m l a = FComputation m l a a (LastOutFacts a)
-type FTransformation m l a = FComputation m l a (Maybe (UniqSM (Graph m l)))
- (Maybe (UniqSM (Graph m l)))
-type FPass m l a = FComputation m l a
- (Txlimit -> DFM a (Answer m l a))
- (Txlimit -> DFM a (Answer m l (LastOutFacts a)))
-
-type FUnlimitedPass m l a = FComputation m l a
- (DFM a (Answer m l a))
- (DFM a (Answer m l (LastOutFacts a)))
-
-{-
-\paragraph{Composing passes}
-
-Both forward and backward engines share a handful of functions for
-composing analyses, transformations, and passes.
-
-We can make an analysis pass, or we can
-combine a related analysis and transformation into a full pass.
--}
-
-anal_b :: BAnalysis m l a -> BPass m l a
-a_t_b :: BAnalysis m l a -> BTransformation m l a -> BPass m l a
-a_ft_b :: BAnalysis m l a -> BFunctionalTransformation m l a -> BPass m l a
-a_ft_b_unlimited
- :: BAnalysis m l a -> BFunctionalTransformation m l a -> BPass m l a
- -- ^ Ignores transaction limits. Could produce a BUnlimitedPass statically,
- -- but that would cost too much code in the implementation for a
- -- static distinction that is not worth so much.
-ignore_transactions_b :: BUnlimitedPass m l a -> BPass m l a
-
-
-
-anal_f :: FAnalysis m l a -> FPass m l a
-a_t_f :: FAnalysis m l a -> FTransformation m l a -> FPass m l a
-
-
-{-
-\paragraph {Running the dataflow engine}
-Every function for running analyses has two forms, because for a
-forward analysis, we supply an entry fact, whereas for a backward
-analysis, we don't need to supply an exit fact (because a graph for a
-procedure doesn't have an exit node).
-It's possible we could make these things more regular.
--}
+-- | A backward rewrite takes the same inputs as a backward transfer,
+-- but instead of producing a fact, it produces a replacement graph or Nothing.
+
+data BackwardRewrites middle last a = BackwardRewrites
+ { br_first :: BlockId -> a -> Maybe (AGraph middle last)
+ , br_middle :: middle -> a -> Maybe (AGraph middle last)
+ , br_last :: last -> (BlockId -> a) -> Maybe (AGraph middle last)
+ , br_exit :: Maybe (AGraph middle last)
+ }
+
+-- | A forward rewrite takes the same inputs as a forward transfer,
+-- but instead of producing a fact, it produces a replacement graph or Nothing.
+
+data ForwardRewrites middle last a = ForwardRewrites
+ { fr_first :: BlockId -> a -> Maybe (AGraph middle last)
+ , fr_middle :: middle -> a -> Maybe (AGraph middle last)
+ , fr_last :: last -> a -> Maybe (AGraph middle last)
+ , fr_exit :: a -> Maybe (AGraph middle last)
+ }
+
+{- ===================== FIXED POINTS =================== -}
+
+-- | The result of combined analysis and transformation is a
+-- solution to the set of dataflow equations together with a 'contained value'.
+-- This solution is a member of type class 'FixedPoint', which is parameterized by
+-- * middle and last nodes 'm' and 'l'
+-- * data flow fact 'fact'
+-- * the type 'a' of the contained value
+--
+-- In practice, the contained value 'zdfFpContents' is either a
+-- rewritten graph, when rewriting, or (), when solving without
+-- rewriting. A function 'zdfFpMap' allows a client to change
+-- the contents without changing other values.
+--
+-- To save space, we provide the solution 'zdfFpFacts' as a mapping
+-- from BlockId to fact; if necessary, facts on edges can be
+-- reconstructed using the transfer functions; this functionality is
+-- intended to be included as the 'zdfDecoratedGraph', but the code
+-- has not yet been implemented.
+--
+-- The solution may also includes a fact 'zdfFpOuputFact', which is
+-- not associated with any label:
+-- * for a backward problem, this is the fact at entry
+-- * for a forward problem, this is the fact at the distinguished exit node,
+-- if such a node is present
+--
+-- For a forward problem only, the solution includes 'zdfFpLastOuts',
+-- which is the set of facts on edges leaving the graph.
+--
+-- The flag 'zdfGraphChanged' tells whether the engine did any rewriting.
+
+class FixedPoint fp where
+ zdfFpContents :: fp m l fact a -> a
+ zdfFpFacts :: fp m l fact a -> BlockEnv fact
+ zdfFpOutputFact :: fp m l fact a -> fact -- entry for backward; exit for forward
+ zdfDecoratedGraph :: fp m l fact a -> Graph (fact, m) (fact, l)
+ zdfGraphChanged :: fp m l fact a -> ChangeFlag
+ zdfFpMap :: (a -> b) -> (fp m l fact a -> fp m l fact b)
+
+-- | The class 'FixedPoint' has two instances: one for forward problems and
+-- one for backward problems. The 'CommonFixedPoint' defines all fields
+-- common to both. (The instance declarations are uninteresting and appear below.)
+
+data CommonFixedPoint m l fact a = FP
+ { fp_facts :: BlockEnv fact
+ , fp_out :: fact -- entry for backward; exit for forward
+ , fp_changed :: ChangeFlag
+ , fp_dec_graph :: Graph (fact, m) (fact, l)
+ , fp_contents :: a
+ }
+
+-- | The common fixed point is sufficient for a backward problem.
+type BackwardFixedPoint = CommonFixedPoint
+
+-- | A forward problem needs the common fields, plus the facts on the outedges.
+data ForwardFixedPoint m l fact a = FFP
+ { ffp_common :: CommonFixedPoint m l fact a
+ , zdfFpLastOuts :: LastOutFacts fact
+ }
+
+
+{- ============== SOLVING AND REWRITING ============== -}
+
+type PassName = String
+
+-- | 'zdfSolveFrom' is an overloaded name that resolves to a pure
+-- analysis with no rewriting. It has only two instances: forward and
+-- backward. Since it needs no rewrites, the type parameters of the
+-- class are transfer functions and the fixed point.
+--
+--
+-- An iterative solver normally starts with the bottom fact at every
+-- node, but it can be useful in other contexts as well. For this
+-- reason the initial set of facts (at labelled blocks only) is a
+-- parameter to the solver.
+--
+-- The constraints on the type signature exist purely for debugging;
+-- they make it possible to prettyprint nodes and facts. The parameter of
+-- type 'PassName' is also used just for debugging.
+--
+-- Note that the result is a fixed point with no contents, that is,
+-- the contents have type ().
+--
+-- The intent of the rest of the type signature should be obvious.
+-- If not, place a skype call to norman-ramsey or complain bitterly
+-- to <norman-ramsey@acm.org>.
+
+class DataflowSolverDirection transfers fixedpt where
+ zdfSolveFrom :: (DebugNodes m l, Outputable a)
+ => BlockEnv a -- ^ Initial facts (unbound == bottom)
+ -> PassName
+ -> DataflowLattice a -- ^ Lattice
+ -> transfers m l a -- ^ Dataflow transfer functions
+ -> a -- ^ Fact flowing in (at entry or exit)
+ -> Graph m l -- ^ Graph to be analyzed
+ -> FuelMonad (fixedpt m l a ()) -- ^ Answers
+ zdfSolveFromL :: (DebugNodes m l, Outputable a)
+ => BlockEnv a -- Initial facts (unbound == bottom)
+ -> PassName
+ -> DataflowLattice a -- Lattice
+ -> transfers m l a -- Dataflow transfer functions
+ -> a -- Fact flowing in (at entry or exit)
+ -> LGraph m l -- Graph to be analyzed
+ -> FuelMonad (fixedpt m l a ()) -- Answers
+ zdfSolveFromL b p l t a g = zdfSolveFrom b p l t a $ quickGraph g
+
+-- There are exactly two instances: forward and backward
+instance DataflowSolverDirection ForwardTransfers ForwardFixedPoint
+ where zdfSolveFrom = solve_f
+
+instance DataflowSolverDirection BackwardTransfers BackwardFixedPoint
+ where zdfSolveFrom = solve_b
+
+
+-- | zdfRewriteFrom is an overloaded name that resolves to an
+-- interleaved analysis and transformation. It too is instantiated in
+-- forward and backward directions.
+--
+-- The type parameters of the class include not only transfer
+-- functions and the fixed point but also rewrites.
+--
+-- The type signature of 'zdfRewriteFrom' is that of 'zdfSolveFrom'
+-- with the rewrites and a rewriting depth as additional parameters,
+-- as well as a different result, which contains a rewritten graph.
+
+class DataflowSolverDirection transfers fixedpt =>
+ DataflowDirection transfers fixedpt rewrites where
+ zdfRewriteFrom :: (DebugNodes m l, Outputable a)
+ => RewritingDepth -- whether to rewrite a rewritten graph
+ -> BlockEnv a -- initial facts (unbound == bottom)
+ -> PassName
+ -> DataflowLattice a
+ -> transfers m l a
+ -> rewrites m l a
+ -> a -- fact flowing in (at entry or exit)
+ -> Graph m l
+ -> FuelMonad (fixedpt m l a (Graph m l))
+
+-- Temporarily lifting from Graph to LGraph -- an experiment to see how we
+-- can eliminate some hysteresis between Graph and LGraph.
+-- Perhaps Graph should be confined to dataflow code.
+-- Trading space for time
+quickGraph :: LastNode l => LGraph m l -> Graph m l
+quickGraph g = Graph (ZLast $ mkBranchNode $ lg_entry g) $ lg_blocks g
+
+quickLGraph :: LastNode l => Graph m l -> FuelMonad (LGraph m l)
+quickLGraph (Graph (ZLast (LastOther l)) blockenv)
+ | isBranchNode l = return $ LGraph (branchNodeTarget l) blockenv
+quickLGraph g = F.lGraphOfGraph g
+
+fixptWithLGraph :: LastNode l => CommonFixedPoint m l fact (Graph m l) ->
+ FuelMonad (CommonFixedPoint m l fact (LGraph m l))
+fixptWithLGraph cfp =
+ do fp_c <- quickLGraph $ fp_contents cfp
+ return $ cfp {fp_contents = fp_c}
+
+ffixptWithLGraph :: LastNode l => ForwardFixedPoint m l fact (Graph m l) ->
+ FuelMonad (ForwardFixedPoint m l fact (LGraph m l))
+ffixptWithLGraph fp =
+ do common <- fixptWithLGraph $ ffp_common fp
+ return $ fp {ffp_common = common}
+
+zdfFRewriteFromL :: (DebugNodes m l, Outputable a)
+ => RewritingDepth -- whether to rewrite a rewritten graph
+ -> BlockEnv a -- initial facts (unbound == bottom)
+ -> PassName
+ -> DataflowLattice a
+ -> ForwardTransfers m l a
+ -> ForwardRewrites m l a
+ -> a -- fact flowing in (at entry or exit)
+ -> LGraph m l
+ -> FuelMonad (ForwardFixedPoint m l a (LGraph m l))
+zdfFRewriteFromL d b p l t r a g@(LGraph _ _) =
+ do fp <- zdfRewriteFrom d b p l t r a $ quickGraph g
+ ffixptWithLGraph fp
+
+zdfBRewriteFromL :: (DebugNodes m l, Outputable a)
+ => RewritingDepth -- whether to rewrite a rewritten graph
+ -> BlockEnv a -- initial facts (unbound == bottom)
+ -> PassName
+ -> DataflowLattice a
+ -> BackwardTransfers m l a
+ -> BackwardRewrites m l a
+ -> a -- fact flowing in (at entry or exit)
+ -> LGraph m l
+ -> FuelMonad (BackwardFixedPoint m l a (LGraph m l))
+zdfBRewriteFromL d b p l t r a g@(LGraph _ _) =
+ do fp <- zdfRewriteFrom d b p l t r a $ quickGraph g
+ fixptWithLGraph fp
+
+
+data RewritingDepth = RewriteShallow | RewriteDeep
+-- When a transformation proposes to rewrite a node,
+-- you can either ask the system to
+-- * "shallow": accept the new graph, analyse it without further rewriting
+-- * "deep": recursively analyse-and-rewrite the new graph
+
+
+-- There are currently four instances, but there could be more
+-- forward, backward (instantiates transfers, fixedpt, rewrites)
+-- Graph, AGraph (instantiates graph)
+
+instance DataflowDirection ForwardTransfers ForwardFixedPoint ForwardRewrites
+ where zdfRewriteFrom = rewrite_f_agraph
+
+instance DataflowDirection BackwardTransfers BackwardFixedPoint BackwardRewrites
+ where zdfRewriteFrom = rewrite_b_agraph
+
+
+{- =================== IMPLEMENTATIONS ===================== -}
+
+
+-----------------------------------------------------------
+-- solve_f: forward, pure
+
+solve_f :: (DebugNodes m l, Outputable a)
+ => BlockEnv a -- initial facts (unbound == bottom)
+ -> PassName
+ -> DataflowLattice a -- lattice
+ -> ForwardTransfers m l a -- dataflow transfer functions
+ -> a
+ -> Graph m l -- graph to be analyzed
+ -> FuelMonad (ForwardFixedPoint m l a ()) -- answers
+solve_f env name lattice transfers in_fact g =
+ runDFM lattice $ fwd_pure_anal name env transfers in_fact g
+
+rewrite_f_agraph :: (DebugNodes m l, Outputable a)
+ => RewritingDepth
+ -> BlockEnv a
+ -> PassName
+ -> DataflowLattice a
+ -> ForwardTransfers m l a
+ -> ForwardRewrites m l a
+ -> a -- fact flowing in (at entry or exit)
+ -> Graph m l
+ -> FuelMonad (ForwardFixedPoint m l a (Graph m l))
+rewrite_f_agraph depth start_facts name lattice transfers rewrites in_fact g =
+ runDFM lattice $
+ do fuel <- fuelRemaining
+ (fp, fuel') <- forward_rew maybeRewriteWithFuel depth start_facts name
+ transfers rewrites in_fact g fuel
+ fuelDecrement name fuel fuel'
+ return fp
+
+areturn :: AGraph m l -> DFM a (Graph m l)
+areturn g = liftToDFM $ liftUniq $ graphOfAGraph g
+
+-- | Here we prefer not simply to slap on 'goto eid' because this
+-- introduces an unnecessary basic block at each rewrite, and we don't
+-- want to stress out the finite map more than necessary
+lgraphToGraph :: LastNode l => LGraph m l -> Graph m l
+lgraphToGraph (LGraph eid blocks) =
+ if flip any (eltsBlockEnv blocks) $ \block -> any (== eid) (succs block) then
+ Graph (ZLast (mkBranchNode eid)) blocks
+ else -- common case: entry is not a branch target
+ let Block _ entry = lookupBlockEnv blocks eid `orElse` panic "missing entry!"
+ in Graph entry (delFromBlockEnv blocks eid)
+
--- | The analysis functions set properties on unique IDs.
+class (Outputable m, Outputable l, LastNode l, Outputable (LGraph m l)) => DebugNodes m l
-run_b_anal :: forall m l a . (DebugNodes m l, LastNode l, Outputable a) =>
- BAnalysis m l a -> LGraph m l -> DFA a ()
-run_f_anal :: forall m l a . (DebugNodes m l, LastNode l, Outputable a) =>
- FAnalysis m l a -> a -> LGraph m l -> DFA a ()
- -- ^ extra parameter is the entry fact
+fwd_pure_anal :: (DebugNodes m l, LastNode l, Outputable a)
+ => PassName
+ -> BlockEnv a
+ -> ForwardTransfers m l a
+ -> a
+ -> Graph m l
+ -> DFM a (ForwardFixedPoint m l a ())
+
+fwd_pure_anal name env transfers in_fact g =
+ do (fp, _) <- anal_f name env transfers panic_rewrites in_fact g panic_fuel
+ return fp
+ where -- definitely a case of "I love lazy evaluation"
+ anal_f = forward_sol (\_ _ -> Nothing) panic_depth
+ panic_rewrites = panic "pure analysis asked for a rewrite function"
+ panic_fuel = panic "pure analysis asked for fuel"
+ panic_depth = panic "pure analysis asked for a rewrite depth"
+
+-----------------------------------------------------------------------
+--
+-- Here beginneth the super-general functions
+--
+-- Think of them as (typechecked) macros
+-- * They are not exported
+--
+-- * They are called by the specialised wrappers
+-- above, and always inlined into their callers
+--
+-- There are four functions, one for each combination of:
+-- Forward, Backward
+-- Solver, Rewriter
+--
+-- A "solver" produces a (DFM f (f, Fuel)),
+-- where f is the fact at entry(Bwd)/exit(Fwd)
+-- and from the DFM you can extract
+-- the BlockId->f
+-- the change-flag
+-- and more besides
+--
+-- A "rewriter" produces a rewritten *Graph* as well
+--
+-- Both constrain their rewrites by
+-- a) Fuel
+-- b) RewritingDepth: shallow/deep
+
+-----------------------------------------------------------------------
+
+type Fuel = OptimizationFuel
+
+forward_sol
+ :: forall m l a .
+ (DebugNodes m l, LastNode l, Outputable a)
+ => (forall a . Fuel -> Maybe a -> Maybe a)
+ -- Squashes proposed rewrites if there is
+ -- no more fuel; OR if we are doing a pure
+ -- analysis, so totally ignore the rewrite
+ -- ie. For pure-analysis the fn is (\_ _ -> Nothing)
+ -> RewritingDepth -- Shallow/deep
+ -> PassName
+ -> BlockEnv a -- Initial set of facts
+ -> ForwardTransfers m l a
+ -> ForwardRewrites m l a
+ -> a -- Entry fact
+ -> Graph m l
+ -> Fuel
+ -> DFM a (ForwardFixedPoint m l a (), Fuel)
+forward_sol check_maybe = forw
+ where
+ forw :: RewritingDepth
+ -> PassName
+ -> BlockEnv a
+ -> ForwardTransfers m l a
+ -> ForwardRewrites m l a
+ -> a
+ -> Graph m l
+ -> Fuel
+ -> DFM a (ForwardFixedPoint m l a (), Fuel)
+ forw rewrite name start_facts transfers rewrites =
+ let anal_f :: DFM a b -> a -> Graph m l -> DFM a b
+ anal_f finish in' g =
+ do { _ <- fwd_pure_anal name emptyBlockEnv transfers in' g; finish }
+
+ solve :: DFM a b -> a -> Graph m l -> Fuel -> DFM a (b, Fuel)
+ solve finish in_fact (Graph entry blockenv) fuel =
+ let blocks = G.postorder_dfs_from blockenv entry
+ set_or_save = mk_set_or_save (isJust . lookupBlockEnv blockenv)
+ set_successor_facts (Block id tail) fuel =
+ do { idfact <- getFact id
+ ; (last_outs, fuel) <- rec_rewrite (fr_first rewrites id idfact)
+ (ft_first_out transfers id idfact)
+ getExitFact (solve_tail tail)
+ (solve_tail tail) idfact fuel
+ ; set_or_save last_outs
+ ; return fuel }
+ in do { (last_outs, fuel) <- solve_tail entry in_fact fuel
+ -- last_outs contains a mix of internal facts, which
+ -- are inputs to 'run', and external facts, which
+ -- are going to be forgotten by 'run'
+ ; set_or_save last_outs
+ ; fuel <- run "forward" name set_successor_facts blocks fuel
+ ; set_or_save last_outs
+ -- Re-set facts that may have been forgotten by run
+ ; b <- finish
+ ; return (b, fuel)
+ }
+ -- The need for both k1 and k2 suggests that maybe there's an opportunity
+ -- for improvement here -- in most cases, they're the same...
+ rec_rewrite rewritten analyzed finish k1 k2 in' fuel =
+ case check_maybe fuel rewritten of -- fr_first rewrites id idfact of
+ Nothing -> k1 analyzed fuel
+ Just g -> do g <- areturn g
+ (a, fuel) <- subAnalysis' $
+ case rewrite of
+ RewriteDeep -> solve finish in' g (oneLessFuel fuel)
+ RewriteShallow -> do { a <- anal_f finish in' g
+ ; return (a, oneLessFuel fuel) }
+ k2 a fuel
+ solve_tail (G.ZTail m t) in' fuel =
+ rec_rewrite (fr_middle rewrites m in') (ft_middle_out transfers m in')
+ getExitFact (solve_tail t) (solve_tail t) in' fuel
+ solve_tail (G.ZLast (LastOther l)) in' fuel =
+ rec_rewrite (fr_last rewrites l in') (ft_last_outs transfers l in')
+ lastOutFacts k k in' fuel
+ where k a b = return (a, b)
+ solve_tail (G.ZLast LastExit) in' fuel =
+ rec_rewrite (fr_exit rewrites in') (ft_exit_out transfers in')
+ lastOutFacts k (\a b -> return (a, b)) in' fuel
+ where k a fuel = do { setExitFact a ; return (LastOutFacts [], fuel) }
+
+ fixed_point in_fact g fuel =
+ do { setAllFacts start_facts
+ ; (a, fuel) <- solve getExitFact in_fact g fuel
+ ; facts <- getAllFacts
+ ; last_outs <- lastOutFacts
+ ; let cfp = FP facts a NoChange (panic "no decoration?!") ()
+ ; let fp = FFP cfp last_outs
+ ; return (fp, fuel)
+ }
+ in fixed_point
--- | Rematerialize results of analysis for use elsewhere. Simply applies a
--- fold function to every edge fact, in reverse postorder dfs. The facts
--- should already have been computed into the monady by run_b_anal or b_rewrite.
-fold_edge_facts_b
- :: LastNode l =>
- (a -> b -> b) -> BAnalysis m l a -> LGraph m l -> (BlockId -> a) -> b -> b
-fold_edge_facts_with_nodes_b :: LastNode l
- => (l -> a -> b -> b) -- ^ inedge to last node
- -> (m -> a -> b -> b) -- ^ inedge to middle node
- -> (BlockId -> a -> b -> b) -- ^ fact at label
- -> BAnalysis m l a -- ^ backwards analysis
- -> LGraph m l -- ^ graph
- -> (BlockId -> a) -- ^ solution to bwd anal
- -> b -> b
--- | It can be useful to refine the results of an existing analysis,
--- or for example to use the outcome of a forward analsysis in a
--- backward analysis. These functions can also be used to compute a
--- fixed point iteratively starting from somewhere other than bottom
--- (as in the reachability analysis done for proc points).
+mk_set_or_save :: (DataflowAnalysis df, Monad (df a), Outputable a) =>
+ (BlockId -> Bool) -> LastOutFacts a -> df a ()
+mk_set_or_save is_local (LastOutFacts l) = mapM_ set_or_save_one l
+ where set_or_save_one (id, a) =
+ if is_local id then setFact id a else addLastOutFact (id, a)
-class (Outputable m, Outputable l, LastNode l, Outputable (LGraph m l)) => DebugNodes m l
-refine_f_anal :: forall m l a . (DebugNodes m l, LastNode l, Outputable a) =>
- FAnalysis m l a -> LGraph m l -> DFA a () -> DFA a ()
-refine_b_anal :: forall m l a . (DebugNodes m l, LastNode l, Outputable a) =>
- BAnalysis m l a -> LGraph m l -> DFA a () -> DFA a ()
+forward_rew
+ :: forall m l a .
+ (DebugNodes m l, LastNode l, Outputable a)
+ => (forall a . Fuel -> Maybe a -> Maybe a)
+ -> RewritingDepth
+ -> BlockEnv a
+ -> PassName
+ -> ForwardTransfers m l a
+ -> ForwardRewrites m l a
+ -> a
+ -> Graph m l
+ -> Fuel
+ -> DFM a (ForwardFixedPoint m l a (Graph m l), Fuel)
+forward_rew check_maybe = forw
+ where
+ solve = forward_sol check_maybe
+ forw :: RewritingDepth
+ -> BlockEnv a
+ -> PassName
+ -> ForwardTransfers m l a
+ -> ForwardRewrites m l a
+ -> a
+ -> Graph m l
+ -> Fuel
+ -> DFM a (ForwardFixedPoint m l a (Graph m l), Fuel)
+ forw depth xstart_facts name transfers rewrites in_factx gx fuelx =
+ let rewrite :: BlockEnv a -> DFM a b
+ -> a -> Graph m l -> Fuel
+ -> DFM a (b, Graph m l, Fuel)
+ rewrite start finish in_fact g fuel =
+ in_fact `seq` g `seq`
+ let Graph entry blockenv = g
+ blocks = G.postorder_dfs_from blockenv entry
+ in do { _ <- solve depth name start transfers rewrites in_fact g fuel
+ ; eid <- freshBlockId "temporary entry id"
+ ; (rewritten, fuel) <-
+ rew_tail (ZFirst eid) in_fact entry emptyBlockEnv fuel
+ ; (rewritten, fuel) <- rewrite_blocks blocks rewritten fuel
+ ; a <- finish
+ ; return (a, lgraphToGraph (LGraph eid rewritten), fuel)
+ }
+ don't_rewrite facts finish in_fact g fuel =
+ do { _ <- solve depth name facts transfers rewrites in_fact g fuel
+ ; a <- finish
+ ; return (a, g, fuel)
+ }
+ inner_rew :: DFM a f -> a -> Graph m l -> Fuel -> DFM a (f, Graph m l, Fuel)
+ inner_rew f i g fu = getAllFacts >>= \facts -> inner_rew' facts f i g fu
+ where inner_rew' = case depth of RewriteShallow -> don't_rewrite
+ RewriteDeep -> rewrite
+ fixed_pt_and_fuel =
+ do { (a, g, fuel) <- rewrite xstart_facts getExitFact in_factx gx fuelx
+ ; facts <- getAllFacts
+ ; changed <- graphWasRewritten
+ ; last_outs <- lastOutFacts
+ ; let cfp = FP facts a changed (panic "no decoration?!") g
+ ; let fp = FFP cfp last_outs
+ ; return (fp, fuel)
+ }
+-- JD: WHY AREN'T WE TAKING ANY FUEL HERE?
+ rewrite_blocks :: [Block m l] -> (BlockEnv (Block m l))
+ -> Fuel -> DFM a (BlockEnv (Block m l), Fuel)
+ rewrite_blocks [] rewritten fuel = return (rewritten, fuel)
+ rewrite_blocks (G.Block id t : bs) rewritten fuel =
+ do let h = ZFirst id
+ a <- getFact id
+ case check_maybe fuel $ fr_first rewrites id a of
+ Nothing -> do { (rewritten, fuel) <-
+ rew_tail h (ft_first_out transfers id a)
+ t rewritten fuel
+ ; rewrite_blocks bs rewritten fuel }
+ Just g -> do { markGraphRewritten
+ ; g <- areturn g
+ ; (outfact, g, fuel) <- inner_rew getExitFact a g fuel
+ ; let (blocks, h) = splice_head' h g
+ ; (rewritten, fuel) <-
+ rew_tail h outfact t (blocks `plusBlockEnv` rewritten) fuel
+ ; rewrite_blocks bs rewritten fuel }
+
+ rew_tail head in' (G.ZTail m t) rewritten fuel =
+ in' `seq` rewritten `seq`
+ my_trace "Rewriting middle node" (ppr m) $
+ case check_maybe fuel $ fr_middle rewrites m in' of
+ Nothing -> rew_tail (G.ZHead head m) (ft_middle_out transfers m in') t
+ rewritten fuel
+ Just g -> do { markGraphRewritten
+ ; g <- areturn g
+ ; (a, g, fuel) <- inner_rew getExitFact in' g fuel
+ ; let (blocks, h) = G.splice_head' head g
+ ; rew_tail h a t (blocks `plusBlockEnv` rewritten) fuel
+ }
+ rew_tail h in' (G.ZLast l) rewritten fuel =
+ in' `seq` rewritten `seq`
+ my_trace "Rewriting last node" (ppr l) $
+ case check_maybe fuel $ either_last rewrites in' l of
+ Nothing -> do check_facts in' l
+ return (insertBlock (zipht h (G.ZLast l)) rewritten, fuel)
+ Just g -> do { markGraphRewritten
+ ; g <- areturn g
+ ; ((), g, fuel) <-
+ my_trace "Just" (ppr g) $ inner_rew (return ()) in' g fuel
+ ; let g' = G.splice_head_only' h g
+ ; return (G.lg_blocks g' `plusBlockEnv` rewritten, fuel)
+ }
+ either_last rewrites in' (LastExit) = fr_exit rewrites in'
+ either_last rewrites in' (LastOther l) = fr_last rewrites l in'
+ check_facts in' (LastOther l) =
+ let LastOutFacts last_outs = ft_last_outs transfers l in'
+ in mapM_ (uncurry checkFactMatch) last_outs
+ check_facts _ LastExit = return ()
+ in fixed_pt_and_fuel
+
+lastOutFacts :: DFM f (LastOutFacts f)
+lastOutFacts = bareLastOutFacts >>= return . LastOutFacts
+
+{- ================================================================ -}
+
+solve_b :: (DebugNodes m l, Outputable a)
+ => BlockEnv a -- initial facts (unbound == bottom)
+ -> PassName
+ -> DataflowLattice a -- lattice
+ -> BackwardTransfers m l a -- dataflow transfer functions
+ -> a -- exit fact
+ -> Graph m l -- graph to be analyzed
+ -> FuelMonad (BackwardFixedPoint m l a ()) -- answers
+solve_b env name lattice transfers exit_fact g =
+ runDFM lattice $ bwd_pure_anal name env transfers g exit_fact
+
+
+rewrite_b_agraph :: (DebugNodes m l, Outputable a)
+ => RewritingDepth
+ -> BlockEnv a
+ -> PassName
+ -> DataflowLattice a
+ -> BackwardTransfers m l a
+ -> BackwardRewrites m l a
+ -> a -- fact flowing in at exit
+ -> Graph m l
+ -> FuelMonad (BackwardFixedPoint m l a (Graph m l))
+rewrite_b_agraph depth start_facts name lattice transfers rewrites exit_fact g =
+ runDFM lattice $
+ do fuel <- fuelRemaining
+ (fp, fuel') <- backward_rew maybeRewriteWithFuel depth start_facts name
+ transfers rewrites g exit_fact fuel
+ fuelDecrement name fuel fuel'
+ return fp
+
+
+
+backward_sol
+ :: forall m l a .
+ (DebugNodes m l, LastNode l, Outputable a)
+ => (forall a . Fuel -> Maybe a -> Maybe a)
+ -> RewritingDepth
+ -> PassName
+ -> BlockEnv a
+ -> BackwardTransfers m l a
+ -> BackwardRewrites m l a
+ -> Graph m l
+ -> a
+ -> Fuel
+ -> DFM a (BackwardFixedPoint m l a (), Fuel)
+backward_sol check_maybe = back
+ where
+ back :: RewritingDepth
+ -> PassName
+ -> BlockEnv a
+ -> BackwardTransfers m l a
+ -> BackwardRewrites m l a
+ -> Graph m l
+ -> a
+ -> Fuel
+ -> DFM a (BackwardFixedPoint m l a (), Fuel)
+ back rewrite name start_facts transfers rewrites =
+ let anal_b :: Graph m l -> a -> DFM a a
+ anal_b g out =
+ do { fp <- bwd_pure_anal name emptyBlockEnv transfers g out
+ ; return $ zdfFpOutputFact fp }
+
+ subsolve :: AGraph m l -> a -> Fuel -> DFM a (a, Fuel)
+ subsolve =
+ case rewrite of
+ RewriteDeep -> \g a fuel ->
+ subAnalysis' $ do { g <- areturn g; solve g a (oneLessFuel fuel) }
+ RewriteShallow -> \g a fuel ->
+ subAnalysis' $ do { g <- areturn g; a <- anal_b g a
+ ; return (a, oneLessFuel fuel) }
+
+ solve :: Graph m l -> a -> Fuel -> DFM a (a, Fuel)
+ solve (Graph entry blockenv) exit_fact fuel =
+ let blocks = reverse $ G.postorder_dfs_from blockenv entry
+ last_in _env (LastExit) = exit_fact
+ last_in env (LastOther l) = bt_last_in transfers l env
+ last_rew _env (LastExit) = br_exit rewrites
+ last_rew env (LastOther l) = br_last rewrites l env
+ set_block_fact block fuel =
+ let (h, l) = G.goto_end (G.unzip block) in
+ do { env <- factsEnv
+ ; (a, fuel) <-
+ case check_maybe fuel $ last_rew env l of
+ Nothing -> return (last_in env l, fuel)
+ Just g -> do g' <- areturn g
+ my_trace "analysis rewrites last node"
+ (ppr l <+> pprGraph g') $
+ subsolve g exit_fact fuel
+ ; _ <- set_head_fact h a fuel
+ ; return fuel }
+
+ in do { fuel <- run "backward" name set_block_fact blocks fuel
+ ; eid <- freshBlockId "temporary entry id"
+ ; fuel <- set_block_fact (Block eid entry) fuel
+ ; a <- getFact eid
+ ; forgetFact eid
+ ; return (a, fuel)
+ }
+
+ set_head_fact (G.ZFirst id) a fuel =
+ case check_maybe fuel $ br_first rewrites id a of
+ Nothing -> do { my_trace "set_head_fact" (ppr id <+> text "=" <+>
+ ppr (bt_first_in transfers id a)) $
+ setFact id $ bt_first_in transfers id a
+ ; return fuel }
+ Just g -> do { g' <- areturn g
+ ; (a, fuel) <- my_trace "analysis rewrites first node"
+ (ppr id <+> pprGraph g') $
+ subsolve g a fuel
+ ; setFact id $ bt_first_in transfers id a
+ ; return fuel
+ }
+ set_head_fact (G.ZHead h m) a fuel =
+ case check_maybe fuel $ br_middle rewrites m a of
+ Nothing -> set_head_fact h (bt_middle_in transfers m a) fuel
+ Just g -> do { g' <- areturn g
+ ; (a, fuel) <- my_trace "analysis rewrites middle node"
+ (ppr m <+> pprGraph g') $
+ subsolve g a fuel
+ ; set_head_fact h a fuel }
+
+ fixed_point g exit_fact fuel =
+ do { setAllFacts start_facts
+ ; (a, fuel) <- solve g exit_fact fuel
+ ; facts <- getAllFacts
+ ; let cfp = FP facts a NoChange (panic "no decoration?!") ()
+ ; return (cfp, fuel)
+ }
+ in fixed_point
+
+bwd_pure_anal :: (DebugNodes m l, LastNode l, Outputable a)
+ => PassName
+ -> BlockEnv a
+ -> BackwardTransfers m l a
+ -> Graph m l
+ -> a
+ -> DFM a (BackwardFixedPoint m l a ())
+
+bwd_pure_anal name env transfers g exit_fact =
+ do (fp, _) <- anal_b name env transfers panic_rewrites g exit_fact panic_fuel
+ return fp
+ where -- another case of "I love lazy evaluation"
+ anal_b = backward_sol (\_ _ -> Nothing) panic_depth
+ panic_rewrites = panic "pure analysis asked for a rewrite function"
+ panic_fuel = panic "pure analysis asked for fuel"
+ panic_depth = panic "pure analysis asked for a rewrite depth"
+
+
+{- ================================================================ -}
+
+backward_rew
+ :: forall m l a .
+ (DebugNodes m l, LastNode l, Outputable a)
+ => (forall a . Fuel -> Maybe a -> Maybe a)
+ -> RewritingDepth
+ -> BlockEnv a
+ -> PassName
+ -> BackwardTransfers m l a
+ -> BackwardRewrites m l a
+ -> Graph m l
+ -> a
+ -> Fuel
+ -> DFM a (BackwardFixedPoint m l a (Graph m l), Fuel)
+backward_rew check_maybe = back
+ where
+ solve = backward_sol check_maybe
+ back :: RewritingDepth
+ -> BlockEnv a
+ -> PassName
+ -> BackwardTransfers m l a
+ -> BackwardRewrites m l a
+ -> Graph m l
+ -> a
+ -> Fuel
+ -> DFM a (BackwardFixedPoint m l a (Graph m l), Fuel)
+ back depth xstart_facts name transfers rewrites gx exit_fact fuelx =
+ let rewrite :: BlockEnv a
+ -> Graph m l -> a -> Fuel
+ -> DFM a (a, Graph m l, Fuel)
+ rewrite start g exit_fact fuel =
+ let Graph entry blockenv = g
+ blocks = reverse $ G.postorder_dfs_from blockenv entry
+ in do { (FP _ in_fact _ _ _, _) <- -- don't drop the entry fact!
+ solve depth name start transfers rewrites g exit_fact fuel
+ --; env <- getAllFacts
+ -- ; my_trace "facts after solving" (ppr env) $ return ()
+ ; eid <- freshBlockId "temporary entry id"
+ ; (rewritten, fuel) <- rewrite_blocks True blocks emptyBlockEnv fuel
+ -- We can't have the fact check fail on the bogus entry, which _may_ change
+ ; (rewritten, fuel) <-
+ rewrite_blocks False [Block eid entry] rewritten fuel
+ ; my_trace "eid" (ppr eid) $ return ()
+ ; my_trace "exit_fact" (ppr exit_fact) $ return ()
+ ; my_trace "in_fact" (ppr in_fact) $ return ()
+ ; return (in_fact, lgraphToGraph (LGraph eid rewritten), fuel)
+ } -- Remember: the entry fact computed by @solve@ accounts for rewriting
+ don't_rewrite facts g exit_fact fuel =
+ do { (fp, _) <-
+ solve depth name facts transfers rewrites g exit_fact fuel
+ ; return (zdfFpOutputFact fp, g, fuel) }
+ inner_rew :: Graph m l -> a -> Fuel -> DFM a (a, Graph m l, Fuel)
+ inner_rew g a f = getAllFacts >>= \facts -> inner_rew' facts g a f
+ where inner_rew' = case depth of RewriteShallow -> don't_rewrite
+ RewriteDeep -> rewrite
+ fixed_pt_and_fuel =
+ do { (a, g, fuel) <- rewrite xstart_facts gx exit_fact fuelx
+ ; facts <- getAllFacts
+ ; changed <- graphWasRewritten
+ ; let fp = FP facts a changed (panic "no decoration?!") g
+ ; return (fp, fuel)
+ }
+ rewrite_blocks :: Bool -> [Block m l] -> (BlockEnv (Block m l))
+ -> Fuel -> DFM a (BlockEnv (Block m l), Fuel)
+ rewrite_blocks check bs rewritten fuel =
+ do { env <- factsEnv
+ ; let rew [] r f = return (r, f)
+ rew (b : bs) r f =
+ do { (r, f) <- rewrite_block check env b r f; rew bs r f }
+ ; rew bs rewritten fuel }
+ rewrite_block check env b rewritten fuel =
+ let (h, l) = G.goto_end (G.unzip b) in
+ case maybeRewriteWithFuel fuel $ either_last env l of
+ Nothing -> propagate check fuel h (last_in env l) (ZLast l) rewritten
+ Just g ->
+ do { markGraphRewritten
+ ; g <- areturn g
+ ; (a, g, fuel) <- inner_rew g exit_fact fuel
+ ; let G.Graph t new_blocks = g
+ ; let rewritten' = new_blocks `plusBlockEnv` rewritten
+ ; propagate check fuel h a t rewritten' -- continue at entry of g
+ }
+ either_last _env (LastExit) = br_exit rewrites
+ either_last env (LastOther l) = br_last rewrites l env
+ last_in _env (LastExit) = exit_fact
+ last_in env (LastOther l) = bt_last_in transfers l env
+ propagate check fuel (ZHead h m) a tail rewritten =
+ case maybeRewriteWithFuel fuel $ br_middle rewrites m a of
+ Nothing ->
+ propagate check fuel h (bt_middle_in transfers m a) (ZTail m tail) rewritten
+ Just g ->
+ do { markGraphRewritten
+ ; g <- areturn g
+ ; my_trace "With Facts" (ppr a) $ return ()
+ ; my_trace " Rewrote middle node"
+ (f4sep [ppr m, text "to", pprGraph g]) $
+ return ()
+ ; (a, g, fuel) <- inner_rew g a fuel
+ ; let Graph t newblocks = G.splice_tail g tail
+ ; my_trace "propagating facts" (ppr a) $
+ propagate check fuel h a t (newblocks `plusBlockEnv` rewritten) }
+ propagate check fuel (ZFirst id) a tail rewritten =
+ case maybeRewriteWithFuel fuel $ br_first rewrites id a of
+ Nothing -> do { if check then
+ checkFactMatch id $ bt_first_in transfers id a
+ else return ()
+ ; return (insertBlock (Block id tail) rewritten, fuel) }
+ Just g ->
+ do { markGraphRewritten
+ ; g <- areturn g
+ ; my_trace "Rewrote first node"
+ (f4sep [ppr id <> colon, text "to", pprGraph g]) $ return ()
+ ; (a, g, fuel) <- inner_rew g a fuel
+ ; if check then checkFactMatch id (bt_first_in transfers id a)
+ else return ()
+ ; let Graph t newblocks = G.splice_tail g tail
+ ; let r = insertBlock (Block id t) (newblocks `plusBlockEnv` rewritten)
+ ; return (r, fuel) }
+ in fixed_pt_and_fuel
+
+{- ================================================================ -}
+
+instance FixedPoint CommonFixedPoint where
+ zdfFpFacts = fp_facts
+ zdfFpOutputFact = fp_out
+ zdfGraphChanged = fp_changed
+ zdfDecoratedGraph = fp_dec_graph
+ zdfFpContents = fp_contents
+ zdfFpMap f (FP fs out ch dg a) = FP fs out ch dg (f a)
+
+instance FixedPoint ForwardFixedPoint where
+ zdfFpFacts = fp_facts . ffp_common
+ zdfFpOutputFact = fp_out . ffp_common
+ zdfGraphChanged = fp_changed . ffp_common
+ zdfDecoratedGraph = fp_dec_graph . ffp_common
+ zdfFpContents = fp_contents . ffp_common
+ zdfFpMap f (FFP fp los) = FFP (zdfFpMap f fp) los
-b_rewrite :: (DebugNodes m l, Outputable a) =>
- BPass m l a -> LGraph m l -> DFM a (LGraph m l)
-f_rewrite :: (DebugNodes m l, LastNode l, Outputable m, Outputable a) =>
- FPass m l a -> a -> LGraph m l -> DFM a (LGraph m l)
- -- ^ extra parameter is the entry fact
--- | If the solution to a problem is already sitting in a monad, we
--- should be able to take a short cut and just rewrite it in one pass.
--- But not yet implemented.
+dump_things :: Bool
+dump_things = False
-{-
-f_rewrite_solved :: (LastNode l, Outputable m, Outputable a) =>
- FPass m l a -> DFM a () -> LGraph m l -> DFM a (LGraph m l)
-b_rewrite_solved :: (LastNode l, Outputable m, Outputable a) =>
- BPass m l a -> DFM a () -> LGraph m l -> DFM a (LGraph m l)
--}
+my_trace :: String -> SDoc -> a -> a
+my_trace = if dump_things then pprTrace else \_ _ a -> a
--- ===================== IMPLEMENTATION ======================--
-- | Here's a function to run an action on blocks until we reach a fixed point.
-run :: (DataflowAnalysis anal, Monad (anal a), Outputable a, DebugNodes m l) =>
- String -> String -> anal a () -> (b -> Block m l -> anal a b) ->
- b -> [Block m l] -> anal a b
-run dir name set_entry do_block b blocks =
- do { set_entry; show_blocks $ iterate (1::Int) }
+run :: (Outputable a, DebugNodes m l) =>
+ String -> String -> (Block m l -> b -> DFM a b) -> [Block m l] -> b -> DFM a b
+run dir name do_block blocks b =
+ do { show_blocks $ iterate (1::Int) }
where
-- N.B. Each iteration starts with the same transaction limit;
-- only the rewrites in the final iteration actually count
- trace_block b block = my_trace "about to do" (text name <+> text "on" <+> ppr (blockId block)) $
- do_block b block
+ trace_block (b, cnt) block =
+ do b' <- my_trace "about to do" (text name <+> text "on" <+>
+ ppr (blockId block) <+> ppr cnt) $
+ do_block block b
+ return (b', cnt + 1)
iterate n =
- do { markFactsUnchanged
- ; b <- foldM trace_block b blocks
+ do { forgetLastOutFacts
+ ; markFactsUnchanged
+ ; (b, _) <- foldM trace_block (b, 0 :: Int) blocks
; changed <- factsStatus
- ; facts <- allFacts
+ ; facts <- getAllFacts
; let depth = 0 -- was nesting depth
; ppIter depth n $
case changed of
my_nest depth sdoc = my_trace "" $ nest (3*depth) sdoc
ppIter depth n = my_nest depth (empty $$ text "*************** iteration" <+> pp_i n)
pp_i n = int n <+> text "of" <+> text name <+> text "on" <+> graphId
- unchanged depth = my_nest depth (text "facts are unchanged")
+ unchanged depth =
+ my_nest depth (text "facts for" <+> graphId <+> text "are unchanged")
- pprFacts depth n env =
- my_nest depth (text "facts for iteration" <+> pp_i n <+> text "are:" $$
- (nest 2 $ vcat $ map pprFact $ ufmToList env))
- pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)
graphId = case blocks of { Block id _ : _ -> ppr id ; [] -> text "<empty>" }
show_blocks = my_trace "Blocks:" (vcat (map pprBlock blocks))
pprBlock (Block id t) = nest 2 (pprFact (id, t))
+ pprFacts depth n env =
+ my_nest depth (text "facts for iteration" <+> pp_i n <+> text "are:" $$
+ (nest 2 $ vcat $ map pprFact $ blockEnvToList env))
+ pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)
-{-
-\subsection{Backward problems}
-
-In a backward problem, we compute \emph{in} facts from \emph{out}
-facts. The analysis gives us [[exit_in]], [[last_in]], [[middle_in]],
-and [[first_in]], each of which computes an \emph{in} fact for one
-kind of node. We provide [[head_in]], which computes the \emph{in}
-fact for a first node followed by zero or more middle nodes.
-
-We don't compute and return the \emph{in} fact for block; instead, we
-use [[setFact]] to attach that fact to the block's unique~ID.
-We iterate until no more facts have changed.
--}
-run_b_anal comp graph =
- refine_b_anal comp graph (return ())
- -- for a backward analysis, everything is initially bottom
-
-refine_b_anal comp graph initial =
- run "backward" (bc_name comp) initial set_block_fact () blocks
- where
- blocks = reverse (postorder_dfs graph)
- set_block_fact () b@(G.Block id _) =
- let (h, l) = G.goto_end (G.unzip b) in
- do env <- factsEnv
- let block_in = head_in h (last_in comp env l) -- 'in' fact for the block
- setFact id block_in
- head_in (G.ZHead h m) out = head_in h (bc_middle_in comp out m)
- head_in (G.ZFirst id) out = bc_first_in comp out id
-
-last_in :: BComputation m l i o -> (BlockId -> i) -> G.ZLast l -> o
-last_in comp env (G.LastOther l) = bc_last_in comp env l
-last_in comp _ (G.LastExit) = bc_exit_in comp
-
------- we can now pass those facts elsewhere
-fold_edge_facts_b f comp graph env z =
- foldl fold_block_facts z (postorder_dfs graph)
- where
- fold_block_facts z b =
- let (h, l) = G.goto_end (G.unzip b)
- in head_fold h (last_in comp env l) z
- head_fold (G.ZHead h m) out z = head_fold h (bc_middle_in comp out m) (f out z)
- head_fold (G.ZFirst id) out z = f (bc_first_in comp out id) (f out z)
-
-fold_edge_facts_with_nodes_b fl fm ff comp graph env z =
- foldl fold_block_facts z (postorder_dfs graph)
- where
- fold_block_facts z b =
- let (h, l) = G.goto_end (G.unzip b)
- in' = last_in comp env l
- z' = case l of { G.LastExit -> z ; G.LastOther l -> fl l in' z }
- in head_fold h in' z'
- head_fold (G.ZHead h m) out z =
- let a = bc_middle_in comp out m
- z' = fm m a z
- in head_fold h a z'
- head_fold (G.ZFirst id) out z =
- let a = bc_first_in comp out id
- z' = ff id a z
- in z'
-
-
--- | In the general case we solve a graph in the context of a larger subgraph.
--- To do this, we need a locally modified computation that allows an
--- ``exit fact'' to flow into the exit node.
-
-comp_with_exit_b :: BComputation m l i (Txlimit -> DFM f (Answer m l o)) -> o ->
- BComputation m l i (Txlimit -> DFM f (Answer m l o))
-comp_with_exit_b comp exit_fact =
- comp { bc_exit_in = \_txlim -> return $ Dataflow $ exit_fact }
-
--- | Given this function, we can now solve a graph simply by doing a
--- backward analysis on the modified computation. Note we have to be
--- very careful with 'Rewrite'. Either a rewrite is going to
--- participate, in which case we mark the graph rerewritten, or we're
--- going to analysis the proposed rewrite and then throw away
--- everything but the answer, in which case it's a 'subAnalysis'. A
--- Rewrite should always use exactly one of these monadic operations.
-
-solve_graph_b ::
- forall m l a . (DebugNodes m l, Outputable a) =>
- BPass m l a -> Txlimit -> G.LGraph m l -> a -> DFM a (Txlimit, a)
-solve_graph_b comp txlim graph exit_fact =
- general_backward (comp_with_exit_b comp exit_fact) txlim graph
- where
- general_backward :: BPass m l a -> Txlimit -> G.LGraph m l -> DFM a (Txlimit, a)
- general_backward comp txlim graph =
- let set_block_fact :: Txlimit -> G.Block m l -> DFM a Txlimit
- set_block_fact txlim b =
- do { (txlim, block_in) <-
- let (h, l) = G.goto_end (G.unzip b) in
- factsEnv >>= \env -> last_in comp env l txlim >>= \x ->
- case x of
- Dataflow a -> head_in txlim h a
- Rewrite g ->
- do { bot <- botFact
- ; g <- lgraphOfGraph g
- ; (txlim, a) <- subAnalysis' $
- solve_graph_b comp (txlim-1) g bot
- ; head_in txlim h a }
- ; my_trace "result of" (text (bc_name comp) <+>
- text "on" <+> ppr (G.blockId b) <+> text "is" <+> ppr block_in) $
- setFact (G.blockId b) block_in
- ; return txlim
- }
- head_in txlim (G.ZHead h m) out =
- bc_middle_in comp out m txlim >>= \x -> case x of
- Dataflow a -> head_in txlim h a
- Rewrite g ->
- do { g <- lgraphOfGraph g
- ; (txlim, a) <- subAnalysis' $ solve_graph_b comp (txlim-1) g out
- ; my_trace "Rewrote middle node" (f4sep [ppr m, text "to", ppr g]) $
- head_in txlim h a }
- head_in txlim (G.ZFirst id) out =
- bc_first_in comp out id txlim >>= \x -> case x of
- Dataflow a -> return (txlim, a)
- Rewrite g -> do { g <- lgraphOfGraph g
- ; subAnalysis' $ solve_graph_b comp (txlim-1) g out }
-
- in do { txlim <-
- run "backward" (bc_name comp) (return ()) set_block_fact txlim blocks
- ; a <- getFact (G.gr_entry graph)
- ; facts <- allFacts
- ; my_trace "Solution to graph after pass 1 is" (pprFacts graph facts a) $
- return (txlim, a) }
-
- blocks = reverse (G.postorder_dfs graph)
- pprFacts g env a = (ppr a <+> text "with") $$ vcat (pprLgraph g : map pprFact (ufmToList env))
- pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)
-
-
-lgraphOfGraph :: G.Graph m l -> DFM f (G.LGraph m l)
-lgraphOfGraph g =
- do id <- freshBlockId "temporary id for dataflow analysis"
- return $ labelGraph id g
-
-labelGraph :: BlockId -> G.Graph m l -> G.LGraph m l
-labelGraph id (Graph tail blocks) = LGraph id (insertBlock (Block id tail) blocks)
-
-{-
-We solve and rewrite in two passes: the first pass iterates to a fixed
-point to reach a dataflow solution, and the second pass uses that
-solution to rewrite the graph.
-
-The
-key job is done by [[propagate]], which propagates a fact of type~[[a]]
-between a head and tail.
-The tail is in final form; the head is still to be rewritten.
--}
-
-solve_and_rewrite_b ::
- forall m l a. (DebugNodes m l, Outputable a) =>
- BPass m l a -> Txlimit -> LGraph m l -> a -> DFM a (Txlimit, a, LGraph m l)
-
-solve_and_rewrite_b comp txlim graph exit_fact =
- do { (_, a) <- solve_graph_b comp txlim graph exit_fact -- pass 1
- ; facts <- allFacts
- ; (txlim, g) <- -- pass 2
- my_trace "Solution to graph after pass 1 is" (pprFacts graph facts) $
- backward_rewrite (comp_with_exit_b comp exit_fact) txlim graph
- ; facts <- allFacts
- ; my_trace "Rewritten graph after pass 2 is" (pprFacts g facts) $
- return (txlim, a, g) }
- where
- pprFacts g env = vcat (pprLgraph g : map pprFact (ufmToList env))
- pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)
- eid = G.gr_entry graph
- backward_rewrite comp txlim graph =
- rewrite_blocks comp txlim emptyBlockEnv $ reverse (G.postorder_dfs graph)
- rewrite_blocks ::
- BPass m l a -> Txlimit ->
- BlockEnv (Block m l) -> [Block m l] -> DFM a (Txlimit,G.LGraph m l)
- rewrite_blocks _comp txlim rewritten [] = return (txlim, G.LGraph eid rewritten)
- rewrite_blocks comp txlim rewritten (b:bs) =
- let rewrite_next_block txlim =
- let (h, l) = G.goto_end (G.unzip b) in
- factsEnv >>= \env -> last_in comp env l txlim >>= \x -> case x of
- Dataflow a -> propagate txlim h a (G.ZLast l) rewritten
- Rewrite g -> -- see Note [Rewriting labelled LGraphs]
- do { bot <- botFact
- ; g <- lgraphOfGraph g
- ; (txlim, a, g') <- solve_and_rewrite_b comp (txlim-1) g bot
- ; let G.Graph t new_blocks = G.remove_entry_label g'
- ; markGraphRewritten
- ; let rewritten' = plusUFM new_blocks rewritten
- ; -- continue at entry of g
- propagate txlim h a t rewritten'
- }
- propagate :: Txlimit -> G.ZHead m -> a -> G.ZTail m l ->
- BlockEnv (Block m l) -> DFM a (Txlimit, G.LGraph m l)
- propagate txlim (G.ZHead h m) out tail rewritten =
- bc_middle_in comp out m txlim >>= \x -> case x of
- Dataflow a -> propagate txlim h a (G.ZTail m tail) rewritten
- Rewrite g ->
- do { g <- lgraphOfGraph g
- ; (txlim, a, g') <- solve_and_rewrite_b comp (txlim-1) g out
- ; markGraphRewritten
- ; let (t, g'') = G.splice_tail g' tail
- ; let rewritten' = plusUFM (G.gr_blocks g'') rewritten
- ; my_trace "Rewrote middle node" (f4sep [ppr m, text "to", ppr g]) $
- propagate txlim h a t rewritten' }
- propagate txlim h@(G.ZFirst id) out tail rewritten =
- bc_first_in comp out id txlim >>= \x -> case x of
- Dataflow a ->
- let b = G.Block id tail in
- do { checkFactMatch id a
- ; rewrite_blocks comp txlim (extendBlockEnv rewritten id b) bs }
- Rewrite fg ->
- do { g <- lgraphOfGraph fg
- ; (txlim, a, g') <- solve_and_rewrite_b comp (txlim-1) g out
- ; markGraphRewritten
- ; let (t, g'') = G.splice_tail g' tail
- ; let rewritten' = plusUFM (G.gr_blocks g'') rewritten
- ; my_trace "Rewrote label " (f4sep [ppr id, text "to", ppr g]) $
- propagate txlim h a t rewritten' }
- in rewrite_next_block txlim
-
-b_rewrite comp g =
- do { txlim <- liftTx txRemaining
- ; bot <- botFact
- ; (txlim', _, gc) <- solve_and_rewrite_b comp txlim g bot
- ; liftTx $ txDecrement (bc_name comp) txlim txlim'
- ; return gc
- }
-
-{-
-This debugging stuff is left over from imperative-land.
-It might be useful one day if I learn how to cheat the IO monad!
-
-debug_b :: (Outputable m, Outputable l, Outputable a) => BPass m l a -> BPass m l a
-
-let debug s (f, comp) =
- let pr = Printf.eprintf in
- let fact dir node a = pr "%s %s for %s = %s\n" f.fact_name dir node (s a) in
- let rewr node g = pr "%s rewrites %s to <not-shown>\n" comp.name node in
- let wrap f nodestring node txlim =
- let answer = f node txlim in
- let () = match answer with
- | Dataflow a -> fact "in " (nodestring node) a
- | Rewrite g -> rewr (nodestring node) g in
- answer in
- let wrapout f nodestring out node txlim =
- fact "out" (nodestring node) out;
- wrap (f out) nodestring node txlim in
- let last_in = wrap comp.last_in (RS.rtl << G.last_instr) in
- let middle_in = wrapout comp.middle_in (RS.rtl << G.mid_instr) in
- let first_in =
- let first = function G.Entry -> "<entry>" | G.Label ((u, l), _, _) -> l in
- wrapout comp.first_in first in
- f, { comp with last_in = last_in; middle_in = middle_in; first_in = first_in; }
--}
-
-anal_b comp = comp { bc_last_in = wrap2 $ bc_last_in comp
- , bc_exit_in = wrap0 $ bc_exit_in comp
- , bc_middle_in = wrap2 $ bc_middle_in comp
- , bc_first_in = wrap2 $ bc_first_in comp }
- where wrap2 f out node _txlim = return $ Dataflow (f out node)
- wrap0 fact _txlim = return $ Dataflow fact
-
-ignore_transactions_b comp =
- comp { bc_last_in = wrap2 $ bc_last_in comp
- , bc_exit_in = wrap0 $ bc_exit_in comp
- , bc_middle_in = wrap2 $ bc_middle_in comp
- , bc_first_in = wrap2 $ bc_first_in comp }
- where wrap2 f out node _txlim = f out node
- wrap0 fact _txlim = fact
-
-answer' :: (b -> DFM f (Graph m l)) -> Txlimit -> Maybe b -> a -> DFM f (Answer m l a)
-answer' lift txlim r a =
- case r of Just gc | txlim > 0 -> do { g <- lift gc; return $ Rewrite g }
- _ -> return $ Dataflow a
-
-unlimited_answer'
- :: (b -> DFM f (Graph m l)) -> Txlimit -> Maybe b -> a -> DFM f (Answer m l a)
-unlimited_answer' lift _txlim r a =
- case r of Just gc -> do { g <- lift gc; return $ Rewrite g }
- _ -> return $ Dataflow a
-
-combine_a_t_with :: (Txlimit -> Maybe b -> a -> DFM a (Answer m l a)) ->
- BAnalysis m l a -> BComputation m l a (Maybe b) ->
- BPass m l a
-combine_a_t_with answer anal tx =
- let last_in env l txlim =
- answer txlim (bc_last_in tx env l) (bc_last_in anal env l)
- exit_in txlim = answer txlim (bc_exit_in tx) (bc_exit_in anal)
- middle_in out m txlim =
- answer txlim (bc_middle_in tx out m) (bc_middle_in anal out m)
- first_in out f txlim =
- answer txlim (bc_first_in tx out f) (bc_first_in anal out f)
- in BComp { bc_name = concat [bc_name anal, " and ", bc_name tx]
- , bc_last_in = last_in, bc_middle_in = middle_in
- , bc_first_in = first_in, bc_exit_in = exit_in }
-
-a_t_b = combine_a_t_with (answer' liftUSM)
-a_ft_b = combine_a_t_with (answer' return)
-a_ft_b_unlimited = combine_a_t_with (unlimited_answer' return)
-
-
--- =============== FORWARD ================
-
--- | We don't compute and return the \emph{in} fact for block; instead, we
--- use [[P.set]] to attach that fact to the block's unique~ID.
--- We iterate until no more facts have changed.
-
-dump_things :: Bool
-dump_things = False
-
-my_trace :: String -> SDoc -> a -> a
-my_trace = if dump_things then pprTrace else \_ _ a -> a
-
-run_f_anal comp entry_fact graph = refine_f_anal comp graph set_entry
- where set_entry = setFact (G.gr_entry graph) entry_fact
-
-refine_f_anal comp graph initial =
- run "forward" (fc_name comp) initial set_successor_facts () blocks
- where blocks = G.postorder_dfs graph
- set_successor_facts () (G.Block id t) =
- let forward in' (G.ZTail m t) = forward (fc_middle_out comp in' m) t
- forward in' (G.ZLast l) = setEdgeFacts (last_outs comp in' l)
- _blockname = if id == G.gr_entry graph then "<entry>" else show id
- in getFact id >>= \a -> forward (fc_first_out comp a id) t
- setEdgeFacts (LastOutFacts fs) = mapM_ setEdgeFact fs
- setEdgeFact (id, a) = setFact id a
-
-last_outs :: FComputation m l i om ol -> i -> G.ZLast l -> ol
-last_outs comp i (G.LastExit) = fc_exit_outs comp i
-last_outs comp i (G.LastOther l) = fc_last_outs comp i l
-
--- | In the general case we solve a graph in the context of a larger subgraph.
--- To do this, we need a locally modified computation that allows an
--- ``exit fact'' to flow out of the exit node. We pass in a fresh BlockId
--- to which the exit fact can flow
-
-comp_with_exit_f :: FPass m l a -> BlockId -> FPass m l a
-comp_with_exit_f comp exit_fact_id = comp { fc_exit_outs = exit_outs }
- where exit_outs in' _txlimit =
- return $ Dataflow $ LastOutFacts [(exit_fact_id, in')]
-
--- | Given [[comp_with_exit_f]], we can now solve a graph simply by doing a
--- forward analysis on the modified computation.
-solve_graph_f ::
- forall m l a . (DebugNodes m l, Outputable a) =>
- FPass m l a -> Txlimit -> G.LGraph m l -> a ->
- DFM a (Txlimit, a, LastOutFacts a)
-solve_graph_f comp txlim g in_fact =
- do { exit_fact_id <- freshBlockId "proxy for exit node"
- ; txlim <- general_forward (comp_with_exit_f comp exit_fact_id) txlim in_fact g
- ; a <- getFact exit_fact_id
- ; outs <- lastOutFacts
- ; forgetFact exit_fact_id -- close space leak
- ; return (txlim, a, LastOutFacts outs) }
- where
- general_forward :: FPass m l a -> Txlimit -> a -> G.LGraph m l -> DFM a Txlimit
- general_forward comp txlim entry_fact graph =
- let blocks = G.postorder_dfs g
- is_local id = isJust $ lookupBlockEnv (G.gr_blocks g) id
- set_or_save :: LastOutFacts a -> DFM a ()
- set_or_save (LastOutFacts l) = mapM_ set_or_save_one l
- set_or_save_one (id, a) =
- if is_local id then setFact id a else addLastOutFact (id, a)
- set_entry = setFact (G.gr_entry graph) entry_fact
-
- set_successor_facts txlim b =
- let set_tail_facts txlim in' (G.ZTail m t) =
- my_trace "Solving middle node" (ppr m) $
- fc_middle_out comp in' m txlim >>= \ x -> case x of
- Dataflow a -> set_tail_facts txlim a t
- Rewrite g ->
- do g <- lgraphOfGraph g
- (txlim, out, last_outs) <- subAnalysis' $
- solve_graph_f comp (txlim-1) g in'
- set_or_save last_outs
- set_tail_facts txlim out t
- set_tail_facts txlim in' (G.ZLast l) =
- last_outs comp in' l txlim >>= \x -> case x of
- Dataflow outs -> do { set_or_save outs; return txlim }
- Rewrite g ->
- do g <- lgraphOfGraph g
- (txlim, _, last_outs) <- subAnalysis' $
- solve_graph_f comp (txlim-1) g in'
- set_or_save last_outs
- return txlim
- G.Block id t = b
- in do idfact <- getFact id
- infact <- fc_first_out comp idfact id txlim
- case infact of Dataflow a -> set_tail_facts txlim a t
- Rewrite g ->
- do g <- lgraphOfGraph g
- (txlim, out, last_outs) <- subAnalysis' $
- solve_graph_f comp (txlim-1) g idfact
- set_or_save last_outs
- set_tail_facts txlim out t
- in run "forward" (fc_name comp) set_entry set_successor_facts txlim blocks
-
-
-
-{-
-We solve and rewrite in two passes: the first pass iterates to a fixed
-point to reach a dataflow solution, and the second pass uses that
-solution to rewrite the graph.
-
-The key job is done by [[propagate]], which propagates a fact of type~[[a]]
-between a head and tail.
-The tail is in final form; the head is still to be rewritten.
--}
-solve_and_rewrite_f ::
- forall m l a . (DebugNodes m l, Outputable a) =>
- FPass m l a -> Txlimit -> LGraph m l -> a -> DFM a (Txlimit, a, LGraph m l)
-solve_and_rewrite_f comp txlim graph in_fact =
- do solve_graph_f comp txlim graph in_fact -- pass 1
- exit_id <- freshBlockId "proxy for exit node"
- (txlim, g) <- forward_rewrite (comp_with_exit_f comp exit_id) txlim graph in_fact
- exit_fact <- getFact exit_id
- return (txlim, exit_fact, g)
-
-forward_rewrite ::
- forall m l a . (DebugNodes m l, Outputable a) =>
- FPass m l a -> Txlimit -> G.LGraph m l -> a -> DFM a (Txlimit, G.LGraph m l)
-forward_rewrite comp txlim graph entry_fact =
- do setFact eid entry_fact
- rewrite_blocks txlim emptyBlockEnv (G.postorder_dfs graph)
- where
- eid = G.gr_entry graph
- is_local id = isJust $ lookupBlockEnv (G.gr_blocks graph) id
- set_or_save :: LastOutFacts a -> DFM a ()
- set_or_save (LastOutFacts l) = mapM_ set_or_save_one l
- set_or_save_one (id, a) =
- if is_local id then checkFactMatch id a
- else panic "set fact outside graph during rewriting pass?!"
-
- rewrite_blocks ::
- Txlimit -> BlockEnv (Block m l) -> [Block m l] -> DFM a (Txlimit, LGraph m l)
- rewrite_blocks txlim rewritten [] = return (txlim, G.LGraph eid rewritten)
- rewrite_blocks txlim rewritten (G.Block id t : bs) =
- do id_fact <- getFact id
- first_out <- fc_first_out comp id_fact id txlim
- case first_out of
- Dataflow a -> propagate txlim (G.ZFirst id) a t rewritten bs
- Rewrite fg -> do { markGraphRewritten
- ; rewrite_blocks (txlim-1) rewritten
- (G.postorder_dfs (labelGraph id fg) ++ bs) }
- propagate :: Txlimit -> G.ZHead m -> a -> G.ZTail m l -> BlockEnv (G.Block m l) ->
- [G.Block m l] -> DFM a (Txlimit, G.LGraph m l)
- propagate txlim h in' (G.ZTail m t) rewritten bs =
- my_trace "Rewriting middle node" (ppr m) $
- do fc_middle_out comp in' m txlim >>= \x -> case x of
- Dataflow a -> propagate txlim (G.ZHead h m) a t rewritten bs
- Rewrite g ->
- my_trace "Rewriting middle node...\n" empty $
- do g <- lgraphOfGraph g
- (txlim, a, g) <- solve_and_rewrite_f comp (txlim-1) g in'
- markGraphRewritten
- my_trace "Rewrite of middle node completed\n" empty $
- let (g', h') = G.splice_head h g in
- propagate txlim h' a t (plusUFM (G.gr_blocks g') rewritten) bs
- propagate txlim h in' (G.ZLast l) rewritten bs =
- do last_outs comp in' l txlim >>= \x -> case x of
- Dataflow outs ->
- do set_or_save outs
- let b = G.zip (G.ZBlock h (G.ZLast l))
- rewrite_blocks txlim (G.insertBlock b rewritten) bs
- Rewrite g ->
- -- could test here that [[exits g = exits (G.Entry, G.ZLast l)]]
- {- if Debug.on "rewrite-last" then
- Printf.eprintf "ZLast node %s rewritten to:\n"
- (RS.rtl (G.last_instr l)); -}
- do g <- lgraphOfGraph g
- (txlim, _, g) <- solve_and_rewrite_f comp (txlim-1) g in'
- markGraphRewritten
- let g' = G.splice_head_only h g
- rewrite_blocks txlim (plusUFM (G.gr_blocks g') rewritten) bs
-
-f_rewrite comp entry_fact g =
- do { txlim <- liftTx txRemaining
- ; (txlim', _, gc) <- solve_and_rewrite_f comp txlim g entry_fact
- ; liftTx $ txDecrement (fc_name comp) txlim txlim'
- ; return gc
- }
-
-
-{-
-debug_f :: (Outputable m, Outputable l, Outputable a) => FPass m l a -> FPass m l a
-
-let debug s (f, comp) =
- let pr = Printf.eprintf in
- let fact dir node a = pr "%s %s for %s = %s\n" f.fact_name dir node (s a) in
- let setter dir node run_sets set =
- run_sets (fun u a -> pr "%s %s for %s = %s\n" f.fact_name dir node (s a); set u a) in
- let rewr node g = pr "%s rewrites %s to <not-shown>\n" comp.name node in
- let wrap f nodestring wrap_answer in' node txlim =
- fact "in " (nodestring node) in';
- wrap_answer (nodestring node) (f in' node txlim)
- and wrap_fact n answer =
- let () = match answer with
- | Dataflow a -> fact "out" n a
- | Rewrite g -> rewr n g in
- answer
- and wrap_setter n answer =
- match answer with
- | Dataflow set -> Dataflow (setter "out" n set)
- | Rewrite g -> (rewr n g; Rewrite g) in
- let middle_out = wrap comp.middle_out (RS.rtl << G.mid_instr) wrap_fact in
- let last_outs = wrap comp.last_outs (RS.rtl << G.last_instr) wrap_setter in
- f, { comp with last_outs = last_outs; middle_out = middle_out; }
--}
-
-anal_f comp = comp { fc_first_out = wrap2 $ fc_first_out comp
- , fc_middle_out = wrap2 $ fc_middle_out comp
- , fc_last_outs = wrap2 $ fc_last_outs comp
- , fc_exit_outs = wrap1 $ fc_exit_outs comp
- }
- where wrap2 f out node _txlim = return $ Dataflow (f out node)
- wrap1 f fact _txlim = return $ Dataflow (f fact)
-
-
-a_t_f anal tx =
- let answer = answer' liftUSM
- first_out in' id txlim =
- answer txlim (fc_first_out tx in' id) (fc_first_out anal in' id)
- middle_out in' m txlim =
- answer txlim (fc_middle_out tx in' m) (fc_middle_out anal in' m)
- last_outs in' l txlim =
- answer txlim (fc_last_outs tx in' l) (fc_last_outs anal in' l)
- exit_outs in' txlim = undefined
- answer txlim (fc_exit_outs tx in') (fc_exit_outs anal in')
- in FComp { fc_name = concat [fc_name anal, " and ", fc_name tx]
- , fc_last_outs = last_outs, fc_middle_out = middle_out
- , fc_first_out = first_out, fc_exit_outs = exit_outs }
-
-
-{- Note [Rewriting labelled LGraphs]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-It's hugely annoying that we get in an LGraph and in order to solve it
-we have to slap on a new label which we then immediately strip off.
-But the alternative is to have all the iterative solvers work on
-Graphs, and then suddenly instead of a single case (ZBlock) every
-solver has to deal with two cases (ZBlock and ZTail). So until
-somebody comes along who is smart enough to do this and still leave
-the code understandable for mortals, it stays as it is.
-
-(A good place to start changing things would be to figure out what is
-the analogue of postorder_dfs for Graphs, and to figure out what
-higher-order functions would do for dealing with the resulting
-sequences of *things*.)
--}
f4sep :: [SDoc] -> SDoc
f4sep [] = fsep []
f4sep (d:ds) = fsep (d : map (nest 4) ds)
+
subAnalysis' :: (Monad (m f), DataflowAnalysis m, Outputable f) =>
m f a -> m f a
subAnalysis' m =
do { a <- subAnalysis $
- do { a <- m; facts <- allFacts
- ; my_trace "after sub-analysis facts are" (pprFacts facts) $
+ do { a <- m; -- facts <- getAllFacts
+ ; -- my_trace "after sub-analysis facts are" (pprFacts facts) $
return a }
- ; facts <- allFacts
- ; my_trace "in parent analysis facts are" (pprFacts facts) $
+ -- ; facts <- getAllFacts
+ ; -- my_trace "in parent analysis facts are" (pprFacts facts) $
return a }
- where pprFacts env = nest 2 $ vcat $ map pprFact $ ufmToList env
- pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)
+ -- where pprFacts env = nest 2 $ vcat $ map pprFact $ blockEnvToList env
+ -- pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)