OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart2js.cps_ir.let_sinking; | 5 library dart2js.cps_ir.loop_hierarchy; |
6 | 6 |
7 import 'cps_ir_nodes.dart'; | 7 import 'cps_ir_nodes.dart'; |
8 import 'optimizers.dart'; | |
9 | |
10 /// Sinks single-use primitives to the use when this is safe and profitable. | |
11 /// | |
12 /// To avoid sinking non-constant primitives into loops, this pass performs a | |
13 /// control-flow analysis to determine the effective nesting of loops. | |
14 /// | |
15 /// In the example below, the value 'p' can be sunk to its use site in the | |
16 /// 'else' branch because that branch is not effectively part of a loop, | |
17 /// despite being lexically nested in a recursive continuation. | |
18 /// | |
19 /// let prim p = getInterceptor(<something>) | |
20 /// let rec kont x = | |
21 /// if (<loop condition>) | |
22 /// <loop body> | |
23 /// InvokeContinuation kont x' | |
24 /// else | |
25 /// <after loop> | |
26 /// return p.foo() | |
27 /// | |
28 class LetSinker extends RecursiveVisitor implements Pass { | |
29 String get passName => 'Let sinking'; | |
30 | |
31 LoopHierarchy loopHierarchy; | |
32 List<Continuation> stack = <Continuation>[]; | |
33 | |
34 /// Maps a sinkable primitive to its loop header. | |
35 Map<Primitive, Continuation> loopHeaderForPrimitive = | |
36 <Primitive, Continuation>{}; | |
37 | |
38 Continuation currentLoopHeader; | |
39 | |
40 void rewrite(FunctionDefinition node) { | |
41 new ParentVisitor().visit(node); | |
42 loopHierarchy = new LoopHierarchy(node); | |
43 visit(node.body); | |
44 } | |
45 | |
46 @override | |
47 Expression traverseLetPrim(LetPrim node) { | |
48 Primitive prim = node.primitive; | |
49 if (prim.hasExactlyOneUse && prim.isSafeForReordering) { | |
50 // This can potentially be sunk. Register the loop header, so when we | |
51 // find the use site, we can check if they are in the same loop. | |
52 loopHeaderForPrimitive[prim] = currentLoopHeader; | |
53 pushAction(() { | |
54 if (node.primitive != null) { | |
55 // The primitive could not be sunk. Try to sink dependencies here. | |
56 visit(node.primitive); | |
57 } else { | |
58 // The primitive was sunk. Destroy the old LetPrim. | |
59 InteriorNode parent = node.parent; | |
60 parent.body = node.body; | |
61 node.body.parent = parent; | |
62 } | |
63 }); | |
64 } else { | |
65 visit(node.primitive); | |
66 } | |
67 | |
68 // Visit the body, wherein this primitive may be sunk to its use site. | |
69 return node.body; | |
70 } | |
71 | |
72 @override | |
73 Expression traverseContinuation(Continuation cont) { | |
74 Continuation oldLoopHeader = currentLoopHeader; | |
75 pushAction(() { | |
76 currentLoopHeader = oldLoopHeader; | |
77 }); | |
78 currentLoopHeader = loopHierarchy.getLoopHeader(cont); | |
79 return cont.body; | |
80 } | |
81 | |
82 void processReference(Reference ref) { | |
83 Definition definition = ref.definition; | |
84 if (definition is Primitive && | |
85 definition is! Parameter && | |
86 definition.hasExactlyOneUse && | |
87 definition.isSafeForReordering) { | |
88 // Check if use is in the same loop. | |
89 Continuation bindingLoop = loopHeaderForPrimitive.remove(definition); | |
90 if (bindingLoop == currentLoopHeader || definition is Constant) { | |
91 // Sink the definition. | |
92 | |
93 Expression use = getEnclosingExpression(ref.parent); | |
94 LetPrim binding = definition.parent; | |
95 binding.primitive = null; // Mark old binding for deletion. | |
96 LetPrim newBinding = new LetPrim(definition); | |
97 definition.parent = newBinding; | |
98 InteriorNode useParent = use.parent; | |
99 useParent.body = newBinding; | |
100 newBinding.body = use; | |
101 use.parent = newBinding; | |
102 newBinding.parent = useParent; | |
103 | |
104 // Now that the final binding location has been found, sink the | |
105 // dependencies of the definition down here as well. | |
106 visit(definition); | |
107 } | |
108 } | |
109 } | |
110 | |
111 Expression getEnclosingExpression(Node node) { | |
112 while (node is! Expression) { | |
113 node = node.parent; | |
114 } | |
115 return node; | |
116 } | |
117 } | |
118 | 8 |
119 /// Determines the effective nesting of loops. | 9 /// Determines the effective nesting of loops. |
120 /// | 10 /// |
121 /// The effective nesting of loops is different from the lexical nesting, since | 11 /// The effective nesting of loops is different from the lexical nesting, since |
122 /// recursive continuations can generally contain all the code following | 12 /// recursive continuations can generally contain all the code following |
123 /// after the loop in addition to the looping code itself. | 13 /// after the loop in addition to the looping code itself. |
124 /// | 14 /// |
125 /// For example, the 'else' branch below is not effectively part of the loop: | 15 /// For example, the 'else' branch below is not effectively part of the loop: |
126 /// | 16 /// |
127 /// let rec kont x = | 17 /// let rec kont x = |
(...skipping 22 matching lines...) Expand all Loading... |
150 /// Parent pointers must be computed for [node]. | 40 /// Parent pointers must be computed for [node]. |
151 LoopHierarchy(FunctionDefinition node) { | 41 LoopHierarchy(FunctionDefinition node) { |
152 _processBlock(node.body, null); | 42 _processBlock(node.body, null); |
153 } | 43 } |
154 | 44 |
155 /// Returns the innermost loop which [cont] is effectively part of. | 45 /// Returns the innermost loop which [cont] is effectively part of. |
156 Continuation getLoopHeader(Continuation cont) { | 46 Continuation getLoopHeader(Continuation cont) { |
157 return cont.isRecursive ? cont : loopTarget[cont]; | 47 return cont.isRecursive ? cont : loopTarget[cont]; |
158 } | 48 } |
159 | 49 |
| 50 /// Returns the innermost loop which the given loop is part of, other |
| 51 /// than itself. |
| 52 Continuation getEnclosingLoop(Continuation loop) { |
| 53 return loopTarget[loop]; |
| 54 } |
| 55 |
160 /// Marks the innermost loop as a subloop of the other loop. | 56 /// Marks the innermost loop as a subloop of the other loop. |
161 /// | 57 /// |
162 /// Returns the innermost loop. | 58 /// Returns the innermost loop. |
163 /// | 59 /// |
164 /// Both continuations, [c1] and [c2] may be null (i.e. no loop). | 60 /// Both continuations, [c1] and [c2] may be null (i.e. no loop). |
165 /// | 61 /// |
166 /// A loop is said to be a subloop of an enclosing loop if it can invoke | 62 /// A loop is said to be a subloop of an enclosing loop if it can invoke |
167 /// that loop recursively. This information is stored in [loopTarget]. | 63 /// that loop recursively. This information is stored in [loopTarget]. |
168 /// | 64 /// |
169 /// This method is only invoked with two distinct loops if there is a | 65 /// This method is only invoked with two distinct loops if there is a |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 target = _markInnerLoop(target, catchLoop); | 144 target = _markInnerLoop(target, catchLoop); |
249 for (Continuation cont in callContinuations) { | 145 for (Continuation cont in callContinuations) { |
250 // Store the loop target on each call continuation in the basic block. | 146 // Store the loop target on each call continuation in the basic block. |
251 // Because we walk over call continuations as part of the basic block | 147 // Because we walk over call continuations as part of the basic block |
252 // traversal, these do not get their loop target set otherwise. | 148 // traversal, these do not get their loop target set otherwise. |
253 loopTarget[cont] = target; | 149 loopTarget[cont] = target; |
254 } | 150 } |
255 return target; | 151 return target; |
256 } | 152 } |
257 } | 153 } |
OLD | NEW |