1 (* Copyright (C) 1999-2006 Henry Cejtin, Matthew Fluet, Suresh
2 * Jagannathan, and Stephen Weeks.
3 * Copyright (C) 1997-2000 NEC Research Institute.
5 * MLton is released under a BSD-style license.
6 * See the file MLton-LICENSE for details.
10 * This pass is based on
11 * Contification Using Dominators, by Fluet and Weeks. ICFP 2001.
14 functor Contify (S: SSA_TRANSFORM_STRUCTS): SSA_TRANSFORM =
22 type t = {cont: Label.t, handler: Handler.t}
24 fun layout {cont, handler}
28 tuple2 (Label.layout, Handler.layout) (cont, handler)
32 (* Return = {Uncalled, Unknown} U Cont U Func
47 of Uncalled => str "Uncalled"
48 | Unknown => str "Unknown"
49 | Cont c => Cont.layout c
50 | Func f => Func.layout f
56 datatype t = T of {node: unit DirectedGraph.Node.t option ref,
58 prefixes: Func.t list ref}
60 fun new () = T {node = ref NONE,
72 val (node', _) = make #node
73 val (rootEdge', _) = make #rootEdge
74 val (prefixes', prefixes) = make #prefixes
76 fun nodeReset (T {node, ...}) = node := NONE
81 datatype t = T of {node: unit DirectedGraph.Node.t option ref,
83 callers: {nontail: (Func.t * Cont.t) list ref,
84 tail: Func.t list ref},
85 callees: {nontail: (Func.t * Cont.t) list ref,
86 tail: Func.t list ref},
88 prefixes: Func.t list ref,
90 replace: {label: Label.t,
91 blocks: Block.t list} option ref,
92 contified: Block.t list list ref}
94 fun new () = T {node = ref NONE,
96 callers = {nontail = ref [], tail = ref []},
97 callees = {nontail = ref [], tail = ref []},
98 A = ref Areturn.Uncalled,
100 finished = ref false,
117 val (node', _) = make #node
118 val (reach', reach) = make #reach
119 val callers' = make' #callers
120 val callees' = make' #callees
122 val (prefixes', prefixes) = make #prefixes
123 val (finished', _) = make #finished
124 val (_, replace) = make #replace
125 val (contified', contified) = make #contified
127 fun nodeReset (T {node, ...}) = node := NONE
130 structure ContFuncGraph =
132 structure Graph = DirectedGraph
133 structure Node = Graph.Node
135 datatype t = ContNode of Cont.t
137 fun newContFuncGraph {getContData: Cont.t -> ContData.t,
138 getFuncData: Func.t -> FuncData.t}
142 = ignore (Graph.addEdge (G, edge))
143 val {get = getNodeInfo : unit Node.t -> t,
144 set = setNodeInfo, ...}
145 = Property.getSetOnce
147 Property.initRaise ("nodeInfo", Node.layout))
150 val node = FuncData.node' (getFuncData f)
155 val n = Graph.newNode G
157 setNodeInfo (n, FuncNode f);
165 val node = ContData.node' (getContData c)
170 val n = Graph.newNode G
172 setNodeInfo (n, ContNode c);
182 then case getNodeInfo n
184 => ContData.nodeReset (getContData c)
186 => FuncData.nodeReset (getFuncData f)
191 getNodeInfo = getNodeInfo,
192 getContNode = getContNode,
193 getFuncNode = getFuncNode,
196 fun newFuncGraph {getFuncData: Func.t -> FuncData.t}
198 val {G, addEdge, getNodeInfo, getFuncNode, reset, ...}
199 = newContFuncGraph {getContData = fn _ => Error.bug "Contify.ContFuncGraph.newFuncGraph",
200 getFuncData = getFuncData}
204 getNodeInfo = fn n => case getNodeInfo n
206 | ContNode _ => Error.bug "Contify.ContFuncGraph.newFuncGraph",
207 getFuncNode = getFuncNode,
212 structure InitReachCallersCallees =
214 structure Graph = DirectedGraph
215 structure DfsParam = Graph.DfsParam
217 (* Define Reach: Func -> Bool as follows:
218 * Reach (f) iff there is a path of calls from fm to f.
220 * Define NontailCallers: Func -> P (Func x Cont) as follows:
221 * NontailCallers (f) = {(g, c) | (g, f, c) in N}
222 * Define TailCallers: Func -> P (Func) as follows:
223 * Callers (f) = {g | (g, f) in T}
224 * Define NontailCallees: Func -> P (Func x Cont) as follows:
225 * NontailCallers (f) = {(g, c) | (f, g, c) in N}
226 * Define TailCallees: Func -> P (Func) as follows:
227 * Callers (f) = {g | (f, g) in T}
229 * Precondition: forall f in Func. (FuncData.node o getFuncData) f = NONE
230 * forall f in Func. (FuncData.callers o getFuncData) f
231 * = {nontail = [], tail = []}
232 * forall f in Func. (FuncData.callees o getFuncData) f
233 * = {nontail = [], tail = []}
234 * Postcondition: FuncData.reach o getFuncData = Reach
235 * #nontail (FuncData.callers o getFuncData)
237 * #tail (FuncData.callers o getFuncData)
239 * #nontail (FuncData.callees o getFuncData)
241 * #tail (FuncData.callees o getFuncData)
244 fun initReachCallersCallees
245 {program = Program.T {functions, main = fm, ...},
246 getFuncData: Func.t -> FuncData.t} : unit
248 val {G, addEdge, getNodeInfo, getFuncNode, reset, ...}
249 = ContFuncGraph.newFuncGraph {getFuncData = getFuncData}
256 val {name = f, blocks, ...} = Function.dest func
257 val callees = FuncData.callees' (getFuncData f)
258 val f_node = getFuncNode f
262 fn Block.T {transfer = Call {func = g, return, ...}, ...}
264 val callers = FuncData.callers' (getFuncData g)
265 val g_node = getFuncNode g
269 (List.push (#nontail callees, (g, c));
270 List.push (#nontail callers, (f, c)))
271 | _ => (List.push (#tail callees, g);
272 List.push (#tail callers, f))
274 addEdge {from = f_node,
281 = DfsParam.finishNode
282 (fn n => FuncData.reach' (getFuncData (getNodeInfo n)) := true)
283 val fm_node = getFuncNode fm
285 Graph.dfsNodes (G, [fm_node], dfs_param);
288 val initReachCallersCallees
289 = Control.trace (Control.Detail, "initReachCallerCallees")
290 initReachCallersCallees
293 structure AnalyzeDom =
295 structure Graph = DirectedGraph
296 structure Node = Graph.Node
298 (* Now define a directed graph G = (Node, Edge) where
299 * Node = Cont U Fun U {Root}
300 * Edge = {(Root, fm)}
301 * U {(Root, c) | c in Cont}
302 * U {(Root, f) | not (Reach (f))}
303 * U {(f, g) | (f, g) in T and Reach (f)}
304 * U {(c, g) | (f, g, c) in N and Reach (f)}
306 * Let D be the dominator tree of G rooted at Root.
307 * For f in Fun, let idom (f) be the parent of f in D.
309 * Define an analysis, A_Dom, based on D as follows:
312 * then if Reach (f) then Unknown else Uncalled
313 * else the ancestor g of f in D such that idom (g) = Root
315 * Precondition: forall c in Cont. (ContData.node o getContData) c = NONE
316 * forall c in Cont. (ContData.rootEdge o getContData) c = false
317 * forall f in Func. (FuncData.node o getFuncData) f = NONE
318 * forall f in Func. (FuncData.reach o getFuncData) f = Reach
319 * Postcondition: FuncData.ADom o getFuncData = A_Dom
320 * forall c in Cont. (ContData.node o getContData) c = NONE
321 * forall f in Func. (FuncData.node o getFuncData) f = NONE
323 fun analyzeDom {program as Program.T {functions, main = fm, ...},
324 getContData: Cont.t -> ContData.t,
325 getFuncData: Func.t -> FuncData.t} : unit
327 datatype z = datatype Areturn.t
329 val {G, addEdge, getNodeInfo, getContNode, getFuncNode, reset, ...}
330 = ContFuncGraph.newContFuncGraph {getContData = getContData,
331 getFuncData = getFuncData}
332 val Root = DirectedGraph.newNode G
334 fun buildGraph () = let
335 val fm_node = getFuncNode fm
337 val _ = addEdge {from = Root, to = fm_node}
338 (* { (Root, f) | fm calls f } *)
340 if !Control.contifyIntoMain
345 Function.dest (Program.mainFunction program)
348 (blocks, fn Block.T {transfer, ...} =>
351 addEdge {from = Root, to = getFuncNode func}
359 val {name = f, blocks, ...} = Function.dest func
360 val f_reach = FuncData.reach (getFuncData f)
361 val f_node = getFuncNode f
366 fn Block.T {transfer = Call {func = g, return, ...}, ...}
367 => if FuncData.reach (getFuncData g)
369 val g_node = getFuncNode g
373 (* When compiling with profiling,
374 * Dead returns are allowed to
375 * have nonempty source stacks
376 * (see type-check.fun). So, we
377 * can't contify functions that
378 * are called with a Dead cont.
380 addEdge {from = Root,
382 | Return.NonTail c =>
384 val c_node = getContNode c
391 else ((* {(Root, c) | c in Cont} *)
392 addEdge {from = Root,
395 (* {(c, g) | (f, g, c) in N
397 addEdge {from = c_node,
401 (* {(f, g) | (f, g) in T
403 addEdge {from = f_node,
408 else (* {(Root, f) | not (Reach (f))} *)
409 addEdge {from = Root,
414 = Control.trace (Control.Detail, "buildGraph") buildGraph
415 val _ = buildGraph ()
417 fun computeDominators () = let
418 val {idom} = Graph.dominators (G, {root = Root})
420 val computeDominators
421 = Control.trace (Control.Detail, "computeDominators") computeDominators
422 val idom = computeDominators ()
424 fun computeADom () = let
428 if Node.equals (parent, Root)
432 | Graph.Unreachable => Error.bug "Contify.AnalyzeDom.ancestor: unreachable"
439 val {name = f, ...} = Function.dest func
440 val FuncData.T {A, reach, node, ...} = getFuncData f
443 val f_node = valOf (!node)
444 datatype z = datatype ContFuncGraph.t
446 if (case idom f_node of
447 Graph.Idom n => Node.equals (n, Root)
449 | Graph.Unreachable => Error.bug "Contify.AnalyzeDom.idom: unreachable")
451 then f_ADom := Unknown
452 else f_ADom := Uncalled
454 (* Use this for the ancestor version *)
455 val l_node = ancestor f_node
456 (* Use this for the parent version *)
457 (* val l_node = idom f_node *)
459 case getNodeInfo l_node
460 of FuncNode g => f_ADom := Func g
461 | ContNode c => f_ADom := Cont c
466 = Control.trace (Control.Detail, "compute ADom") computeADom
467 val _ = computeADom ()
469 val _ = reset (fn n => not (Node.equals (n, Root)))
474 = Control.trace (Control.Detail, "analyzeDom") analyzeDom
477 structure Transform =
480 * Precondition: forall c in Cont. (ContData.node o getContData) c = NONE
481 * forall c in Cont. (ContData.prefixes o getContData) c = []
482 * forall f in Func. (FuncData.node o getFuncData) f = NONE
483 * FuncData.A o getFuncData = A
484 * where A is a safe analysis
485 * FuncData.callers o getFuncData
486 * = {nontail = NontailCallers, tail = TailCallers}
487 * FuncData.callees o getFuncData
488 * = {nontail = NontailCallees, tail = TailCallees}
489 * forall f in Func. (FuncData.prefixes o getFuncData) f = []
490 * forall f in Func. (FuncData.finished o getFuncData) f = false
491 * forall f in Func. (FuncData.replace o getFuncData) f = NONE
492 * Postcondition: forall c in Cont. (ContData.node o getContData) c = NONE
493 * forall f in Func. (FuncData.node o getFuncData) f = NONE
495 fun transform {program = Program.T {datatypes, globals, functions, main},
496 getFuncData: Func.t -> FuncData.t,
497 getContData: Cont.t -> ContData.t} : Program.t
499 datatype z = datatype Areturn.t
501 (* For functions turned into continuations,
502 * record their args, blocks, and new name.
513 ...} = Function.dest func
514 val FuncData.T {A, replace, ...} = getFuncData f
516 val _ = Control.diagnostics
519 in display (seq [str "A(",
522 Areturn.layout (!A)])
528 val f_label = Label.newString (Func.originalName f)
529 val _ = Control.diagnostics
532 in display (seq [Func.layout f,
534 Label.layout f_label])
537 = (Block.T {label = f_label,
539 statements = Vector.new0 (),
540 transfer = Goto {dst = f_start,
541 args = Vector.new0 ()}})::
542 (Vector.toList f_blocks)
544 replace := SOME {label = f_label,
546 List.push(prefixes, f)
552 | Cont c => contify (ContData.prefixes' (getContData c))
553 | Func g => contify (FuncData.prefixes' (getFuncData g))
557 Trace.trace3 ("Contify.Transform.addFuncs",
559 List.layout Func.layout,
562 val traceTransBlock =
563 Trace.trace3 ("Contify.Transform.transBlock",
565 Label.layout o Block.label,
568 (* Walk over all functions, removing those that aren't top level,
569 * and descening those that are, inserting local functions
571 * - turn tail calls into nontail calls
572 * - turn returns into gotos
573 * - turn raises into gotos
575 fun addFuncPrefixes (f: Func.t,
579 val prefixes = FuncData.prefixes (getFuncData g)
580 val _ = Control.diagnostics
583 in display (seq [str "addFuncPrefixes: ",
588 List.layout Func.layout prefixes])
591 addFuncs (f, prefixes, c)
593 and addContPrefixes (f: Func.t,
597 val prefixes = ContData.prefixes (getContData r)
598 val _ = Control.diagnostics
601 in display (seq [str "addContPrefixes: ",
606 List.layout Func.layout prefixes])
610 addFuncs (f, prefixes, Return.compose (c, Return.NonTail r))
612 and addFuncs arg : unit =
620 val finished = FuncData.finished' (getFuncData g)
624 else (addFuncPrefixes(f, g, c);
627 #blocks (valOf (FuncData.replace (getFuncData g))),
632 and addBlocks (f: Func.t,
633 blocks: Block.t list,
636 val contified' = List.map(blocks,
637 fn block => transBlock (f, block, c))
638 val contified = FuncData.contified' (getFuncData f)
640 List.push(contified, contified')
642 and transBlock arg: Block.t =
645 Block.T {label, args, statements, transfer},
650 of Call {func, args, return}
652 Return.NonTail r => addContPrefixes (f, r, c)
654 case FuncData.replace (getFuncData func) of
655 NONE => Call {func = func,
657 return = Return.compose (c, return)}
658 | SOME {label, ...} =>
659 Goto {dst = label, args = args})
662 of Return.NonTail {cont, ...}
663 => Goto {dst = cont, args = xs}
667 of Return.NonTail {handler = Handler.Handle handler, ...}
668 => Goto {dst = handler, args = xs}
672 Block.T {label = label,
674 statements = statements,
678 val shrink = shrinkFunction {globals = globals}
682 (functions, [], fn (func, ac) =>
686 mayInline = f_mayInline,
690 start = f_start} = Function.dest func
692 case FuncData.A (getFuncData f)
695 val _ = addFuncPrefixes (f, f, Return.Tail)
698 (f_blocks, fn block =>
699 transBlock (f, block, Return.Tail))
702 (FuncData.contified (getFuncData f))
704 = Vector.fromList (List.concat f_blocks)
706 shrink (Function.new {args = f_args,
708 mayInline = f_mayInline,
719 = Program.T {datatypes = datatypes,
721 functions = functions,
727 = Control.trace (Control.Detail, "transform") transform
730 fun transform (program as Program.T _)
732 val {get = getLabelInfo : Label.t -> (Handler.t * ContData.t) list ref,
735 (Label.plist, Property.initFun (fn _ => ref []))
736 val getContData : Cont.t -> ContData.t
739 val l = getLabelInfo cont
741 case List.peek (!l, fn (handler', _) =>
742 Handler.equals (handler, handler'))
743 of SOME (_, cd) => cd
745 val cd = ContData.new ()
746 val _ = List.push(l, (handler, cd))
751 val {get = getFuncData : Func.t -> FuncData.t, ...}
752 = Property.get (Func.plist,
754 (fn _ => FuncData.new ()))
756 val _ = InitReachCallersCallees.initReachCallersCallees
758 getFuncData = getFuncData}
759 val _ = AnalyzeDom.analyzeDom
761 getContData = getContData,
762 getFuncData = getFuncData}
763 val program = Transform.transform
765 getContData = getContData,
766 getFuncData = getFuncData}
767 val _ = Program.clearTop program