1 {-# LANGUAGE MultiParamTypeClasses #-}
4 , BComputation(..), BAnalysis, BTransformation, BFunctionalTransformation
5 , BPass, BUnlimitedPass
6 , FComputation(..), FAnalysis, FTransformation, FPass, FUnlimitedPass
9 , anal_b, a_t_b, a_ft_b, a_ft_b_unlimited, ignore_transactions_b
11 , run_b_anal, run_f_anal
12 , refine_f_anal, refine_b_anal, fold_edge_facts_b, fold_edge_facts_with_nodes_b
13 , b_rewrite, f_rewrite
14 , solve_graph_b, solve_graph_f
20 import ZipCfg hiding (freshBlockId) -- use version from DFMonad
21 import qualified ZipCfg as G
33 \section{A very polymorphic infrastructure for dataflow problems}
35 This module presents a framework for solving iterative dataflow
37 There are two major submodules: one for forward problems and another
38 for backward problems.
39 Both modules incorporate the composition framework developed by
40 Lerner, Grove, and Chambers.
41 They also support a \emph{transaction limit}, which enables the
42 binary-search debugging technique developed by Whalley and Davidson
43 under the name \emph{vpoiso}.
44 Transactions may either be known to the individual dataflow solvers or
45 may be managed by the framework.
48 -- | In the composition framework, a pass either produces a dataflow
49 -- fact or proposes to rewrite the graph. To make life easy for the
50 -- clients, the rewrite is given in unlabelled form, but we use
51 -- labelled form internally throughout, because it greatly simplifies
52 -- the implementation not to have the first block be a special case
55 data Answer m l a = Dataflow a | Rewrite (Graph m l)
60 \subsection {Descriptions of dataflow passes}
62 \paragraph{Passes for backward dataflow problems}
64 The computation of a fact is the basis of a dataflow pass.
65 A~computation takes not one but two type parameters:
68 Type parameter [['i]] is an input, from which it should be possible to
69 derived a dataflow fact of interest.
70 For example, [['i]] might be equal to a fact, or it might be a tuple
71 of which one element is a fact.
73 Type parameter [['o]] is an output, or possibly a function from
76 Backward analyses compute [[in]] facts (facts on inedges).
77 <<exported types for backward analyses>>=
81 data BComputation middle last input output = BComp
83 , bc_exit_in :: output
84 , bc_last_in :: (BlockId -> input) -> last -> output
85 , bc_middle_in :: input -> middle -> output
86 , bc_first_in :: input -> BlockId -> output
89 -- | From these elements we build several kinds of passes:
90 -- * A pure analysis computes a fact, using that fact as input and output.
91 -- * A pure transformation computes no facts but only changes the graph.
92 -- * A fully general pass both computes a fact and rewrites the graph,
93 -- respecting the current transaction limit.
95 type BAnalysis m l a = BComputation m l a a
96 type BTransformation m l a = BComputation m l a (Maybe (UniqSM (Graph m l)))
97 type BFunctionalTransformation m l a = BComputation m l a (Maybe (Graph m l))
99 type BPass m l a = BComputation m l a (OptimizationFuel -> DFM a (Answer m l a))
100 type BUnlimitedPass m l a = BComputation m l a ( DFM a (Answer m l a))
103 \paragraph{Passes for forward dataflow problems}
105 A forward dataflow pass has a similar structure, but the details are
106 different. In particular, the output fact from a [[last]] node has a
107 higher-order representation: it takes a function that mutates a
108 [[uid]] to account for the new fact, then performs the necessary
109 mutation on every successor of the last node. We therefore have two
110 kinds of type parameter for outputs: output from a [[middle]] node
111 is~[[outmid]], and output from a [[last]] node is~[[outlast]].
114 data FComputation middle last input outmid outlast = FComp
116 , fc_first_out :: input -> BlockId -> outmid
117 , fc_middle_out :: input -> middle -> outmid
118 , fc_last_outs :: input -> last -> outlast
119 , fc_exit_outs :: input -> outlast
122 -- | The notions of analysis, pass, and transformation are analogous to the
125 newtype LastOutFacts a = LastOutFacts [(BlockId, a)]
126 -- ^ These are facts flowing out of a last node to the node's successors.
127 -- They are either to be set (if they pertain to the graph currently
128 -- under analysis) or propagated out of a sub-analysis
130 type FAnalysis m l a = FComputation m l a a (LastOutFacts a)
131 type FTransformation m l a = FComputation m l a (Maybe (UniqSM (Graph m l)))
132 (Maybe (UniqSM (Graph m l)))
133 type FPass m l a = FComputation m l a
134 (OptimizationFuel -> DFM a (Answer m l a))
135 (OptimizationFuel -> DFM a (Answer m l (LastOutFacts a)))
137 type FUnlimitedPass m l a = FComputation m l a
138 (DFM a (Answer m l a))
139 (DFM a (Answer m l (LastOutFacts a)))
142 \paragraph{Composing passes}
144 Both forward and backward engines share a handful of functions for
145 composing analyses, transformations, and passes.
147 We can make an analysis pass, or we can
148 combine a related analysis and transformation into a full pass.
151 anal_b :: BAnalysis m l a -> BPass m l a
152 a_t_b :: BAnalysis m l a -> BTransformation m l a -> BPass m l a
153 a_ft_b :: BAnalysis m l a -> BFunctionalTransformation m l a -> BPass m l a
155 :: BAnalysis m l a -> BFunctionalTransformation m l a -> BPass m l a
156 -- ^ Ignores transaction limits. Could produce a BUnlimitedPass statically,
157 -- but that would cost too much code in the implementation for a
158 -- static distinction that is not worth so much.
159 ignore_transactions_b :: BUnlimitedPass m l a -> BPass m l a
163 anal_f :: FAnalysis m l a -> FPass m l a
164 a_t_f :: FAnalysis m l a -> FTransformation m l a -> FPass m l a
168 \paragraph {Running the dataflow engine}
170 Every function for running analyses has two forms, because for a
171 forward analysis, we supply an entry fact, whereas for a backward
172 analysis, we don't need to supply an exit fact (because a graph for a
173 procedure doesn't have an exit node).
174 It's possible we could make these things more regular.
177 -- | The analysis functions set properties on unique IDs.
179 run_b_anal :: (DebugNodes m l, LastNode l, Outputable a) =>
180 BAnalysis m l a -> LGraph m l -> DFA a ()
181 run_f_anal :: (DebugNodes m l, LastNode l, Outputable a) =>
182 FAnalysis m l a -> a -> LGraph m l -> DFA a ()
183 -- ^ extra parameter is the entry fact
185 -- | Rematerialize results of analysis for use elsewhere. Simply applies a
186 -- fold function to every edge fact, in reverse postorder dfs. The facts
187 -- should already have been computed into the monady by run_b_anal or b_rewrite.
190 (a -> b -> b) -> BAnalysis m l a -> LGraph m l -> (BlockId -> a) -> b -> b
192 fold_edge_facts_with_nodes_b :: LastNode l
193 => (l -> a -> b -> b) -- ^ inedge to last node
194 -> (m -> a -> b -> b) -- ^ inedge to middle node
195 -> (BlockId -> a -> b -> b) -- ^ fact at label
196 -> BAnalysis m l a -- ^ backwards analysis
197 -> LGraph m l -- ^ graph
198 -> (BlockId -> a) -- ^ solution to bwd anal
202 -- | It can be useful to refine the results of an existing analysis,
203 -- or for example to use the outcome of a forward analsysis in a
204 -- backward analysis. These functions can also be used to compute a
205 -- fixed point iteratively starting from somewhere other than bottom
206 -- (as in the reachability analysis done for proc points).
208 class (Outputable m, Outputable l, LastNode l, Outputable (LGraph m l)) => DebugNodes m l
210 refine_f_anal :: (DebugNodes m l, LastNode l, Outputable a) =>
211 FAnalysis m l a -> LGraph m l -> DFA a () -> DFA a ()
213 refine_b_anal :: (DebugNodes m l, LastNode l, Outputable a) =>
214 BAnalysis m l a -> LGraph m l -> DFA a () -> DFA a ()
216 b_rewrite :: (DebugNodes m l, Outputable a) =>
217 BPass m l a -> LGraph m l -> DFM a (LGraph m l)
218 f_rewrite :: (DebugNodes m l, LastNode l, Outputable m, Outputable a) =>
219 FPass m l a -> a -> LGraph m l -> DFM a (LGraph m l)
220 -- ^ extra parameter is the entry fact
222 -- | If the solution to a problem is already sitting in a monad, we
223 -- should be able to take a short cut and just rewrite it in one pass.
224 -- But not yet implemented.
227 f_rewrite_solved :: (LastNode l, Outputable m, Outputable a) =>
228 FPass m l a -> DFM a () -> LGraph m l -> DFM a (LGraph m l)
229 b_rewrite_solved :: (LastNode l, Outputable m, Outputable a) =>
230 BPass m l a -> DFM a () -> LGraph m l -> DFM a (LGraph m l)
233 -- ===================== IMPLEMENTATION ======================--
235 -- | Here's a function to run an action on blocks until we reach a fixed point.
236 run :: (DataflowAnalysis anal, Monad (anal a), Outputable a, DebugNodes m l) =>
237 String -> String -> anal a () -> (b -> Block m l -> anal a b) ->
238 b -> [Block m l] -> anal a b
239 run dir name set_entry do_block b blocks =
240 do { set_entry; show_blocks $ iterate (1::Int) }
242 -- N.B. Each iteration starts with the same transaction limit;
243 -- only the rewrites in the final iteration actually count
244 trace_block b block = my_trace "about to do" (text name <+> text "on" <+> ppr (blockId block)) $
247 do { markFactsUnchanged
248 ; b <- foldM trace_block b blocks
249 ; changed <- factsStatus
251 ; let depth = 0 -- was nesting depth
254 NoChange -> unchanged depth $ return b
256 pprFacts depth n facts $
257 if n < 1000 then iterate (n+1)
260 msg n = concat [name, " didn't converge in ", show n, " " , dir,
262 my_nest depth sdoc = my_trace "" $ nest (3*depth) sdoc
263 ppIter depth n = my_nest depth (empty $$ text "*************** iteration" <+> pp_i n)
264 pp_i n = int n <+> text "of" <+> text name <+> text "on" <+> graphId
265 unchanged depth = my_nest depth (text "facts are unchanged")
267 pprFacts depth n env =
268 my_nest depth (text "facts for iteration" <+> pp_i n <+> text "are:" $$
269 (nest 2 $ vcat $ map pprFact $ ufmToList env))
270 pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)
271 graphId = case blocks of { Block id _ : _ -> ppr id ; [] -> text "<empty>" }
272 show_blocks = my_trace "Blocks:" (vcat (map pprBlock blocks))
273 pprBlock (Block id t) = nest 2 (pprFact (id, t))
276 \subsection{Backward problems}
278 In a backward problem, we compute \emph{in} facts from \emph{out}
279 facts. The analysis gives us [[exit_in]], [[last_in]], [[middle_in]],
280 and [[first_in]], each of which computes an \emph{in} fact for one
281 kind of node. We provide [[head_in]], which computes the \emph{in}
282 fact for a first node followed by zero or more middle nodes.
284 We don't compute and return the \emph{in} fact for block; instead, we
285 use [[setFact]] to attach that fact to the block's unique~ID.
286 We iterate until no more facts have changed.
288 run_b_anal comp graph =
289 refine_b_anal comp graph (return ())
290 -- for a backward analysis, everything is initially bottom
292 refine_b_anal comp graph initial =
293 run "backward" (bc_name comp) initial set_block_fact () blocks
295 blocks = reverse (postorder_dfs graph)
296 set_block_fact () b@(G.Block id _) =
297 let (h, l) = G.goto_end (G.unzip b) in
299 let block_in = head_in h (last_in comp env l) -- 'in' fact for the block
301 head_in (G.ZHead h m) out = head_in h (bc_middle_in comp out m)
302 head_in (G.ZFirst id) out = bc_first_in comp out id
304 last_in :: BComputation m l i o -> (BlockId -> i) -> G.ZLast l -> o
305 last_in comp env (G.LastOther l) = bc_last_in comp env l
306 last_in comp _ (G.LastExit) = bc_exit_in comp
308 ------ we can now pass those facts elsewhere
309 fold_edge_facts_b f comp graph env z =
310 foldl fold_block_facts z (postorder_dfs graph)
312 fold_block_facts z b =
313 let (h, l) = G.goto_end (G.unzip b)
314 in head_fold h (last_in comp env l) z
315 head_fold (G.ZHead h m) out z = head_fold h (bc_middle_in comp out m) (f out z)
316 head_fold (G.ZFirst id) out z = f (bc_first_in comp out id) (f out z)
318 fold_edge_facts_with_nodes_b fl fm ff comp graph env z =
319 foldl fold_block_facts z (postorder_dfs graph)
321 fold_block_facts z b =
322 let (h, l) = G.goto_end (G.unzip b)
323 in' = last_in comp env l
324 z' = case l of { G.LastExit -> z ; G.LastOther l -> fl l in' z }
325 in head_fold h in' z'
326 head_fold (G.ZHead h m) out z =
327 let a = bc_middle_in comp out m
330 head_fold (G.ZFirst id) out z =
331 let a = bc_first_in comp out id
336 -- | In the general case we solve a graph in the context of a larger subgraph.
337 -- To do this, we need a locally modified computation that allows an
338 -- ``exit fact'' to flow into the exit node.
340 comp_with_exit_b :: BComputation m l i (OptimizationFuel -> DFM f (Answer m l o)) -> o ->
341 BComputation m l i (OptimizationFuel -> DFM f (Answer m l o))
342 comp_with_exit_b comp exit_fact =
343 comp { bc_exit_in = \_fuel -> return $ Dataflow $ exit_fact }
345 -- | Given this function, we can now solve a graph simply by doing a
346 -- backward analysis on the modified computation. Note we have to be
347 -- very careful with 'Rewrite'. Either a rewrite is going to
348 -- participate, in which case we mark the graph rerewritten, or we're
349 -- going to analysis the proposed rewrite and then throw away
350 -- everything but the answer, in which case it's a 'subAnalysis'. A
351 -- Rewrite should always use exactly one of these monadic operations.
354 (DebugNodes m l, Outputable a) =>
355 BPass m l a -> OptimizationFuel -> G.LGraph m l -> a -> DFM a (OptimizationFuel, a)
356 solve_graph_b comp fuel graph exit_fact =
357 general_backward (comp_with_exit_b comp exit_fact) fuel graph
359 -- general_backward :: BPass m l a -> OptimizationFuel -> G.LGraph m l -> DFM a (OptimizationFuel, a)
360 general_backward comp fuel graph =
361 let -- set_block_fact :: OptimizationFuel -> G.Block m l -> DFM a OptimizationFuel
362 set_block_fact fuel b =
363 do { (fuel, block_in) <-
364 let (h, l) = G.goto_end (G.unzip b) in
365 factsEnv >>= \env -> last_in comp env l fuel >>= \x ->
367 Dataflow a -> head_in fuel h a
370 ; (fuel, a) <- subAnalysis' $
371 solve_graph_b_g comp (fuel-1) g bot
373 ; my_trace "result of" (text (bc_name comp) <+>
374 text "on" <+> ppr (G.blockId b) <+> text "is" <+> ppr block_in) $
375 setFact (G.blockId b) block_in
378 head_in fuel (G.ZHead h m) out =
379 bc_middle_in comp out m fuel >>= \x -> case x of
380 Dataflow a -> head_in fuel h a
382 do { (fuel, a) <- subAnalysis' $ solve_graph_b_g comp (fuel-1) g out
383 ; my_trace "Rewrote middle node"
384 (f4sep [ppr m, text "to", pprGraph g]) $
386 head_in fuel (G.ZFirst id) out =
387 bc_first_in comp out id fuel >>= \x -> case x of
388 Dataflow a -> return (fuel, a)
389 Rewrite g -> do { subAnalysis' $ solve_graph_b_g comp (fuel-1) g out }
392 run "backward" (bc_name comp) (return ()) set_block_fact fuel blocks
393 ; a <- getFact (G.lg_entry graph)
395 ; my_trace "Solution to graph after pass 1 is" (pprFacts graph facts a) $
398 blocks = reverse (G.postorder_dfs graph)
399 pprFacts g env a = (ppr a <+> text "with") $$ vcat (pprLgraph g : map pprFact (ufmToList env))
400 pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)
403 (DebugNodes m l, Outputable a) =>
404 BPass m l a -> OptimizationFuel -> G.Graph m l -> a -> DFM a (OptimizationFuel, a)
405 solve_graph_b_g comp fuel graph exit_fact =
406 do { g <- lgraphOfGraph graph ; solve_graph_b comp fuel g exit_fact }
409 lgraphOfGraph :: G.Graph m l -> DFM f (G.LGraph m l)
411 do id <- freshBlockId "temporary id for dataflow analysis"
412 return $ labelGraph id g
414 labelGraph :: BlockId -> G.Graph m l -> G.LGraph m l
415 labelGraph id (Graph tail blocks) = LGraph id (insertBlock (Block id tail) blocks)
417 -- | We can remove the entry label of an LGraph and remove
418 -- it, leaving a Graph. Notice that this operation is NOT SAFE if a
419 -- block within the LGraph branches to the entry point. It should
420 -- be used only to complement 'lgraphOfGraph' above.
422 remove_entry_label :: LGraph m l -> Graph m l
423 remove_entry_label g =
424 let FGraph e (ZBlock (ZFirst id tail)) others = entry g
425 in ASSERT (id == e) Graph tail others
428 We solve and rewrite in two passes: the first pass iterates to a fixed
429 point to reach a dataflow solution, and the second pass uses that
430 solution to rewrite the graph.
433 key job is done by [[propagate]], which propagates a fact of type~[[a]]
434 between a head and tail.
435 The tail is in final form; the head is still to be rewritten.
438 solve_and_rewrite_b ::
439 (DebugNodes m l, Outputable a) =>
440 BPass m l a -> OptimizationFuel -> LGraph m l -> a -> DFM a (OptimizationFuel, a, LGraph m l)
441 solve_and_rewrite_b_graph ::
442 (DebugNodes m l, Outputable a) =>
443 BPass m l a -> OptimizationFuel -> Graph m l -> a -> DFM a (OptimizationFuel, a, Graph m l)
446 solve_and_rewrite_b comp fuel graph exit_fact =
447 do { (_, a) <- solve_graph_b comp fuel graph exit_fact -- pass 1
449 ; (fuel, g) <- -- pass 2
450 my_trace "Solution to graph after pass 1 is" (pprFacts graph facts) $
451 backward_rewrite (comp_with_exit_b comp exit_fact) fuel graph
453 ; my_trace "Rewritten graph after pass 2 is" (pprFacts g facts) $
454 return (fuel, a, g) }
456 pprFacts g env = vcat (pprLgraph g : map pprFact (ufmToList env))
457 pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)
458 eid = G.lg_entry graph
459 backward_rewrite comp fuel graph =
460 rewrite_blocks comp fuel emptyBlockEnv $ reverse (G.postorder_dfs graph)
462 -- BPass m l a -> OptimizationFuel ->
463 -- BlockEnv (Block m l) -> [Block m l] -> DFM a (OptimizationFuel,G.LGraph m l)
464 rewrite_blocks _comp fuel rewritten [] = return (fuel, G.LGraph eid rewritten)
465 rewrite_blocks comp fuel rewritten (b:bs) =
466 let rewrite_next_block fuel =
467 let (h, l) = G.goto_end (G.unzip b) in
468 factsEnv >>= \env -> last_in comp env l fuel >>= \x -> case x of
469 Dataflow a -> propagate fuel h a (G.ZLast l) rewritten
471 do { markGraphRewritten
473 ; (fuel, a, g') <- solve_and_rewrite_b_graph comp (fuel-1) g bot
474 ; let G.Graph t new_blocks = g'
475 ; let rewritten' = new_blocks `plusUFM` rewritten
476 ; propagate fuel h a t rewritten' -- continue at entry of g'
478 -- propagate :: OptimizationFuel -- Number of rewrites permitted
479 -- -> G.ZHead m -- Part of current block yet to be rewritten
480 -- -> a -- Fact on edge between head and tail
481 -- -> G.ZTail m l -- Part of current block already rewritten
482 -- -> BlockEnv (Block m l) -- Blocks already rewritten
483 -- -> DFM a (OptimizationFuel, G.LGraph m l)
484 propagate fuel (G.ZHead h m) out tail rewritten =
485 bc_middle_in comp out m fuel >>= \x -> case x of
486 Dataflow a -> propagate fuel h a (G.ZTail m tail) rewritten
488 do { markGraphRewritten
489 ; (fuel, a, g') <- solve_and_rewrite_b_graph comp (fuel-1) g out
490 ; let G.Graph t newblocks = G.splice_tail g' tail
491 ; my_trace "Rewrote middle node"
492 (f4sep [ppr m, text "to", pprGraph g']) $
493 propagate fuel h a t (newblocks `plusUFM` rewritten) }
494 propagate fuel h@(G.ZFirst id) out tail rewritten =
495 bc_first_in comp out id fuel >>= \x -> case x of
497 let b = G.Block id tail in
498 do { checkFactMatch id a
499 ; rewrite_blocks comp fuel (extendBlockEnv rewritten id b) bs }
501 do { markGraphRewritten
502 ; (fuel, a, g') <- solve_and_rewrite_b_graph comp (fuel-1) g out
503 ; let G.Graph t newblocks = G.splice_tail g' tail
504 ; my_trace "Rewrote label " (f4sep [ppr id,text "to",pprGraph g])$
505 propagate fuel h a t (newblocks `plusUFM` rewritten) }
506 in rewrite_next_block fuel
508 {- Note [Rewriting labelled LGraphs]
509 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
510 It's hugely annoying that we get in an LGraph and in order to solve it
511 we have to slap on a new label which we then immediately strip off.
512 But the alternative is to have all the iterative solvers work on
513 Graphs, and then suddenly instead of a single case (ZBlock) every
514 solver has to deal with two cases (ZBlock and ZTail). So until
515 somebody comes along who is smart enough to do this and still leave
516 the code understandable for mortals, it stays as it is.
518 (One part of the solution will be postorder_dfs_from_except.)
521 solve_and_rewrite_b_graph comp fuel graph exit_fact =
522 do g <- lgraphOfGraph graph
523 (fuel, a, g') <- solve_and_rewrite_b comp fuel g exit_fact
524 return (fuel, a, remove_entry_label g')
527 do { fuel <- liftTx txRemaining
529 ; (fuel', _, gc) <- solve_and_rewrite_b comp fuel g bot
530 ; liftTx $ txDecrement (bc_name comp) fuel fuel'
535 This debugging stuff is left over from imperative-land.
536 It might be useful one day if I learn how to cheat the IO monad!
538 debug_b :: (Outputable m, Outputable l, Outputable a) => BPass m l a -> BPass m l a
540 let debug s (f, comp) =
541 let pr = Printf.eprintf in
542 let fact dir node a = pr "%s %s for %s = %s\n" f.fact_name dir node (s a) in
543 let rewr node g = pr "%s rewrites %s to <not-shown>\n" comp.name node in
544 let wrap f nodestring node fuel =
545 let answer = f node fuel in
546 let () = match answer with
547 | Dataflow a -> fact "in " (nodestring node) a
548 | Rewrite g -> rewr (nodestring node) g in
550 let wrapout f nodestring out node fuel =
551 fact "out" (nodestring node) out;
552 wrap (f out) nodestring node fuel in
553 let last_in = wrap comp.last_in (RS.rtl << G.last_instr) in
554 let middle_in = wrapout comp.middle_in (RS.rtl << G.mid_instr) in
556 let first = function G.Entry -> "<entry>" | G.Label ((u, l), _, _) -> l in
557 wrapout comp.first_in first in
558 f, { comp with last_in = last_in; middle_in = middle_in; first_in = first_in; }
561 anal_b comp = comp { bc_last_in = wrap2 $ bc_last_in comp
562 , bc_exit_in = wrap0 $ bc_exit_in comp
563 , bc_middle_in = wrap2 $ bc_middle_in comp
564 , bc_first_in = wrap2 $ bc_first_in comp }
565 where wrap2 f out node _fuel = return $ Dataflow (f out node)
566 wrap0 fact _fuel = return $ Dataflow fact
568 ignore_transactions_b comp =
569 comp { bc_last_in = wrap2 $ bc_last_in comp
570 , bc_exit_in = wrap0 $ bc_exit_in comp
571 , bc_middle_in = wrap2 $ bc_middle_in comp
572 , bc_first_in = wrap2 $ bc_first_in comp }
573 where wrap2 f out node _fuel = f out node
574 wrap0 fact _fuel = fact
576 answer' :: (b -> DFM f (Graph m l)) -> OptimizationFuel -> Maybe b -> a -> DFM f (Answer m l a)
577 answer' lift fuel r a =
578 case r of Just gc | fuel > 0 -> do { g <- lift gc; return $ Rewrite g }
579 _ -> return $ Dataflow a
582 :: (b -> DFM f (Graph m l)) -> OptimizationFuel -> Maybe b -> a -> DFM f (Answer m l a)
583 unlimited_answer' lift _fuel r a =
584 case r of Just gc -> do { g <- lift gc; return $ Rewrite g }
585 _ -> return $ Dataflow a
587 combine_a_t_with :: (OptimizationFuel -> Maybe b -> a -> DFM a (Answer m l a)) ->
588 BAnalysis m l a -> BComputation m l a (Maybe b) ->
590 combine_a_t_with answer anal tx =
591 let last_in env l fuel =
592 answer fuel (bc_last_in tx env l) (bc_last_in anal env l)
593 exit_in fuel = answer fuel (bc_exit_in tx) (bc_exit_in anal)
594 middle_in out m fuel =
595 answer fuel (bc_middle_in tx out m) (bc_middle_in anal out m)
596 first_in out f fuel =
597 answer fuel (bc_first_in tx out f) (bc_first_in anal out f)
598 in BComp { bc_name = concat [bc_name anal, " and ", bc_name tx]
599 , bc_last_in = last_in, bc_middle_in = middle_in
600 , bc_first_in = first_in, bc_exit_in = exit_in }
602 a_t_b = combine_a_t_with (answer' liftUSM)
603 a_ft_b = combine_a_t_with (answer' return)
604 a_ft_b_unlimited = combine_a_t_with (unlimited_answer' return)
607 -- =============== FORWARD ================
609 -- | We don't compute and return the \emph{in} fact for block; instead, we
610 -- use [[P.set]] to attach that fact to the block's unique~ID.
611 -- We iterate until no more facts have changed.
616 my_trace :: String -> SDoc -> a -> a
617 my_trace = if dump_things then pprTrace else \_ _ a -> a
619 run_f_anal comp entry_fact graph = refine_f_anal comp graph set_entry
620 where set_entry = setFact (G.lg_entry graph) entry_fact
622 refine_f_anal comp graph initial =
623 run "forward" (fc_name comp) initial set_successor_facts () blocks
624 where blocks = G.postorder_dfs graph
625 set_successor_facts () (G.Block id t) =
626 let forward in' (G.ZTail m t) = forward (fc_middle_out comp in' m) t
627 forward in' (G.ZLast l) = setEdgeFacts (last_outs comp in' l)
628 _blockname = if id == G.lg_entry graph then "<entry>" else show id
629 in getFact id >>= \a -> forward (fc_first_out comp a id) t
630 setEdgeFacts (LastOutFacts fs) = mapM_ setEdgeFact fs
631 setEdgeFact (id, a) = setFact id a
633 last_outs :: FComputation m l i om ol -> i -> G.ZLast l -> ol
634 last_outs comp i (G.LastExit) = fc_exit_outs comp i
635 last_outs comp i (G.LastOther l) = fc_last_outs comp i l
637 -- | In the general case we solve a graph in the context of a larger subgraph.
638 -- To do this, we need a locally modified computation that allows an
639 -- ``exit fact'' to flow out of the exit node. We pass in a fresh BlockId
640 -- to which the exit fact can flow
642 comp_with_exit_f :: FPass m l a -> BlockId -> FPass m l a
643 comp_with_exit_f comp exit_fact_id = comp { fc_exit_outs = exit_outs }
644 where exit_outs in' _fuel = return $ Dataflow $ LastOutFacts [(exit_fact_id, in')]
646 -- | Given [[comp_with_exit_f]], we can now solve a graph simply by doing a
647 -- forward analysis on the modified computation.
649 (DebugNodes m l, Outputable a) =>
650 FPass m l a -> OptimizationFuel -> G.LGraph m l -> a ->
651 DFM a (OptimizationFuel, a, LastOutFacts a)
652 solve_graph_f comp fuel g in_fact =
653 do { exit_fact_id <- freshBlockId "proxy for exit node"
654 ; fuel <- general_forward (comp_with_exit_f comp exit_fact_id) fuel in_fact g
655 ; a <- getFact exit_fact_id
656 ; outs <- lastOutFacts
657 ; forgetFact exit_fact_id -- close space leak
658 ; return (fuel, a, LastOutFacts outs) }
660 -- general_forward :: FPass m l a -> OptimizationFuel -> a -> G.LGraph m l -> DFM a OptimizationFuel
661 general_forward comp fuel entry_fact graph =
662 let blocks = G.postorder_dfs g
663 is_local id = isJust $ lookupBlockEnv (G.lg_blocks g) id
664 -- set_or_save :: LastOutFacts a -> DFM a ()
665 set_or_save (LastOutFacts l) = mapM_ set_or_save_one l
666 set_or_save_one (id, a) =
667 if is_local id then setFact id a else addLastOutFact (id, a)
668 set_entry = setFact (G.lg_entry graph) entry_fact
670 set_successor_facts fuel b =
671 let set_tail_facts fuel in' (G.ZTail m t) =
672 my_trace "Solving middle node" (ppr m) $
673 fc_middle_out comp in' m fuel >>= \ x -> case x of
674 Dataflow a -> set_tail_facts fuel a t
676 do (fuel, out, last_outs) <-
677 subAnalysis' $ solve_graph_f_g comp (fuel-1) g in'
678 set_or_save last_outs
679 set_tail_facts fuel out t
680 set_tail_facts fuel in' (G.ZLast l) =
681 last_outs comp in' l fuel >>= \x -> case x of
682 Dataflow outs -> do { set_or_save outs; return fuel }
684 do (fuel, _, last_outs) <-
685 subAnalysis' $ solve_graph_f_g comp (fuel-1) g in'
686 set_or_save last_outs
689 in do idfact <- getFact id
690 infact <- fc_first_out comp idfact id fuel
691 case infact of Dataflow a -> set_tail_facts fuel a t
693 do (fuel, out, last_outs) <- subAnalysis' $
694 solve_graph_f_g comp (fuel-1) g idfact
695 set_or_save last_outs
696 set_tail_facts fuel out t
697 in run "forward" (fc_name comp) set_entry set_successor_facts fuel blocks
700 (DebugNodes m l, Outputable a) =>
701 FPass m l a -> OptimizationFuel -> G.Graph m l -> a ->
702 DFM a (OptimizationFuel, a, LastOutFacts a)
703 solve_graph_f_g comp fuel graph in_fact =
704 do { g <- lgraphOfGraph graph ; solve_graph_f comp fuel g in_fact }
708 We solve and rewrite in two passes: the first pass iterates to a fixed
709 point to reach a dataflow solution, and the second pass uses that
710 solution to rewrite the graph.
712 The key job is done by [[propagate]], which propagates a fact of type~[[a]]
713 between a head and tail.
714 The tail is in final form; the head is still to be rewritten.
716 solve_and_rewrite_f ::
717 (DebugNodes m l, Outputable a) =>
718 FPass m l a -> OptimizationFuel -> LGraph m l -> a ->
719 DFM a (OptimizationFuel, a, LGraph m l)
720 solve_and_rewrite_f comp fuel graph in_fact =
721 do solve_graph_f comp fuel graph in_fact -- pass 1
722 exit_id <- freshBlockId "proxy for exit node"
723 (fuel, g) <- forward_rewrite (comp_with_exit_f comp exit_id) fuel graph in_fact
724 exit_fact <- getFact exit_id
725 return (fuel, exit_fact, g)
727 solve_and_rewrite_f_graph ::
728 (DebugNodes m l, Outputable a) =>
729 FPass m l a -> OptimizationFuel -> Graph m l -> a ->
730 DFM a (OptimizationFuel, a, Graph m l)
731 solve_and_rewrite_f_graph comp fuel graph in_fact =
732 do g <- lgraphOfGraph graph
733 (fuel, a, g') <- solve_and_rewrite_f comp fuel g in_fact
734 return (fuel, a, remove_entry_label g')
737 (DebugNodes m l, Outputable a) =>
738 FPass m l a -> OptimizationFuel -> G.LGraph m l -> a ->
739 DFM a (OptimizationFuel, G.LGraph m l)
740 forward_rewrite comp fuel graph entry_fact =
741 do setFact eid entry_fact
742 rewrite_blocks fuel emptyBlockEnv (G.postorder_dfs graph)
744 eid = G.lg_entry graph
745 is_local id = isJust $ lookupBlockEnv (G.lg_blocks graph) id
746 -- set_or_save :: LastOutFacts a -> DFM a ()
747 set_or_save (LastOutFacts l) = mapM_ set_or_save_one l
748 set_or_save_one (id, a) =
749 if is_local id then checkFactMatch id a
750 else panic "set fact outside graph during rewriting pass?!"
753 -- OptimizationFuel -> BlockEnv (Block m l) -> [Block m l] -> DFM a (OptimizationFuel, LGraph m l)
754 rewrite_blocks fuel rewritten [] = return (fuel, G.LGraph eid rewritten)
755 rewrite_blocks fuel rewritten (G.Block id t : bs) =
756 do id_fact <- getFact id
757 first_out <- fc_first_out comp id_fact id fuel
759 Dataflow a -> propagate fuel (G.ZFirst id) a t rewritten bs
760 Rewrite g -> do { markGraphRewritten
761 ; rewrite_blocks (fuel-1) rewritten
762 (G.postorder_dfs (labelGraph id g) ++ bs) }
763 -- propagate :: OptimizationFuel -> G.ZHead m -> a -> G.ZTail m l -> BlockEnv (G.Block m l) ->
764 -- [G.Block m l] -> DFM a (OptimizationFuel, G.LGraph m l)
765 propagate fuel h in' (G.ZTail m t) rewritten bs =
766 my_trace "Rewriting middle node" (ppr m) $
767 do fc_middle_out comp in' m fuel >>= \x -> case x of
768 Dataflow a -> propagate fuel (G.ZHead h m) a t rewritten bs
770 do markGraphRewritten
771 (fuel, a, g) <- solve_and_rewrite_f_graph comp (fuel-1) g in'
772 let (blocks, h') = G.splice_head' h g
773 propagate fuel h' a t (blocks `plusUFM` rewritten) bs
774 propagate fuel h in' (G.ZLast l) rewritten bs =
775 do last_outs comp in' l fuel >>= \x -> case x of
778 let b = G.zip (G.ZBlock h (G.ZLast l))
779 rewrite_blocks fuel (G.insertBlock b rewritten) bs
781 do markGraphRewritten
782 (fuel, _, g) <- solve_and_rewrite_f_graph comp (fuel-1) g in'
783 let g' = G.splice_head_only' h g
784 rewrite_blocks fuel (G.lg_blocks g' `plusUFM` rewritten) bs
786 f_rewrite comp entry_fact g =
787 do { fuel <- liftTx txRemaining
788 ; (fuel', _, gc) <- solve_and_rewrite_f comp fuel g entry_fact
789 ; liftTx $ txDecrement (fc_name comp) fuel fuel'
795 debug_f :: (Outputable m, Outputable l, Outputable a) => FPass m l a -> FPass m l a
797 let debug s (f, comp) =
798 let pr = Printf.eprintf in
799 let fact dir node a = pr "%s %s for %s = %s\n" f.fact_name dir node (s a) in
800 let setter dir node run_sets set =
801 run_sets (fun u a -> pr "%s %s for %s = %s\n" f.fact_name dir node (s a); set u a) in
802 let rewr node g = pr "%s rewrites %s to <not-shown>\n" comp.name node in
803 let wrap f nodestring wrap_answer in' node fuel =
804 fact "in " (nodestring node) in';
805 wrap_answer (nodestring node) (f in' node fuel)
806 and wrap_fact n answer =
807 let () = match answer with
808 | Dataflow a -> fact "out" n a
809 | Rewrite g -> rewr n g in
811 and wrap_setter n answer =
813 | Dataflow set -> Dataflow (setter "out" n set)
814 | Rewrite g -> (rewr n g; Rewrite g) in
815 let middle_out = wrap comp.middle_out (RS.rtl << G.mid_instr) wrap_fact in
816 let last_outs = wrap comp.last_outs (RS.rtl << G.last_instr) wrap_setter in
817 f, { comp with last_outs = last_outs; middle_out = middle_out; }
820 anal_f comp = comp { fc_first_out = wrap2 $ fc_first_out comp
821 , fc_middle_out = wrap2 $ fc_middle_out comp
822 , fc_last_outs = wrap2 $ fc_last_outs comp
823 , fc_exit_outs = wrap1 $ fc_exit_outs comp
825 where wrap2 f out node _fuel = return $ Dataflow (f out node)
826 wrap1 f fact _fuel = return $ Dataflow (f fact)
830 let answer = answer' liftUSM
831 first_out in' id fuel =
832 answer fuel (fc_first_out tx in' id) (fc_first_out anal in' id)
833 middle_out in' m fuel =
834 answer fuel (fc_middle_out tx in' m) (fc_middle_out anal in' m)
835 last_outs in' l fuel =
836 answer fuel (fc_last_outs tx in' l) (fc_last_outs anal in' l)
837 exit_outs in' fuel = undefined
838 answer fuel (fc_exit_outs tx in') (fc_exit_outs anal in')
839 in FComp { fc_name = concat [fc_name anal, " and ", fc_name tx]
840 , fc_last_outs = last_outs, fc_middle_out = middle_out
841 , fc_first_out = first_out, fc_exit_outs = exit_outs }
844 f4sep :: [SDoc] -> SDoc
846 f4sep (d:ds) = fsep (d : map (nest 4) ds)
848 subAnalysis' :: (Monad (m f), DataflowAnalysis m, Outputable f) =>
851 do { a <- subAnalysis $
852 do { a <- m; facts <- allFacts
853 ; my_trace "after sub-analysis facts are" (pprFacts facts) $
856 ; my_trace "in parent analysis facts are" (pprFacts facts) $
858 where pprFacts env = nest 2 $ vcat $ map pprFact $ ufmToList env
859 pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)