OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library dart2js.cps_ir.let_sinking; | |
6 | |
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, see | |
14 /// [LoopHierarchy]. | |
floitsch
2015/07/17 17:19:45
I think it's always nice to have an example. (does
asgerf
2015/07/20 09:10:56
Added an example based on the one from LoopHierarc
| |
15 class LetSinker extends RecursiveVisitor implements Pass { | |
16 String get passName => 'Let sinking'; | |
17 | |
18 LoopHierarchy loopHierarchy; | |
19 List<Continuation> stack = <Continuation>[]; | |
20 Set<LetPrim> sunkNodes = new Set<LetPrim>(); | |
21 | |
22 void rewrite(FunctionDefinition node) { | |
23 new ParentVisitor().visit(node); | |
24 loopHierarchy = new LoopHierarchy(node); | |
floitsch
2015/07/17 17:19:45
I'm ok this way, but have a *slight* preference to
asgerf
2015/07/20 09:10:56
Acknowledged.
I usually do that if the class is c
| |
25 visit(node.body); | |
26 } | |
27 | |
28 void visitLetPrim(LetPrim node) { | |
29 // Visit the body, wherein this primitive may be sunk to its use site. | |
30 visit(node.body); | |
31 | |
32 if (node.primitive != null) { | |
33 // The primitive could not be sunk. Sink dependencies to this location. | |
34 visit(node.primitive); | |
35 } else { | |
36 // The primitive was sunk. Destroy the old LetPrim. | |
37 InteriorNode parent = node.parent; | |
38 parent.body = node.body; | |
39 node.body.parent = parent; | |
40 } | |
41 } | |
42 | |
43 void processReference(Reference ref) { | |
44 Definition definition = ref.definition; | |
45 if (definition is Primitive && | |
46 definition is! Parameter && | |
47 definition.hasExactlyOneUse && | |
48 definition.isSafeForReordering) { | |
49 // Check if use is in the same loop. | |
50 LetPrim binding = definition.parent; | |
51 Continuation bindingLoop = loopHierarchy.getLoopHeader(binding); | |
52 Expression use = getEnclosingExpression(ref.parent); | |
53 Continuation useLoop = loopHierarchy.getLoopHeader(use); | |
54 if (bindingLoop == useLoop || definition is Constant) { | |
55 // Sink the definition. | |
56 | |
57 binding.primitive = null; // Mark old binding for deletion. | |
58 LetPrim newBinding = new LetPrim(definition); | |
59 definition.parent = newBinding; | |
60 InteriorNode useParent = use.parent; | |
61 useParent.body = newBinding; | |
62 newBinding.body = use; | |
63 use.parent = newBinding; | |
64 newBinding.parent = useParent; | |
65 | |
66 // Now that the final binding location has been found, sink the | |
67 // dependencies of the definition down here as well. | |
68 visit(definition); | |
69 } | |
70 } | |
71 } | |
72 | |
73 Expression getEnclosingExpression(Node node) { | |
74 while (node is! Expression) { | |
75 node = node.parent; | |
76 } | |
77 return node; | |
78 } | |
79 } | |
80 | |
81 /// Determines the effective nesting of loops. | |
82 /// | |
83 /// The effective nesting of loops is different from the lexical nesting, since | |
84 /// recursive continuations can generally contain all the code following | |
85 /// after the loop in addition to the looping code itself. | |
86 /// | |
87 /// For example, the 'else' branch below is not effectively part of the loop: | |
88 /// | |
89 /// let rec kont x = | |
90 /// if (<loop condition>) | |
91 /// <loop body> | |
92 /// InvokeContinuation kont x' | |
93 /// else | |
94 /// <after loop> | |
95 /// return p.foo() | |
96 /// | |
97 /// We use the term "loop" to mean recursive continuation. | |
98 /// The `null` value is used to represent a context not part of any loop. | |
99 class LoopHierarchy { | |
100 /// Nesting depth of the given loop. | |
101 Map<Continuation, int> loopDepth = <Continuation, int>{}; | |
102 | |
103 /// The innermost loop (other than itself) that may be invoked recursively | |
104 /// as a result of invoking the given continuation. | |
105 Map<Continuation, Continuation> loopTarget = <Continuation, Continuation>{}; | |
106 | |
107 /// Current nesting depth. | |
108 int currentDepth = 0; | |
109 | |
110 /// Computes the loop loop hierarchy for the given function. | |
111 /// | |
112 /// Parent pointers must be computed for [node]. | |
113 LoopHierarchy(FunctionDefinition node) { | |
114 _processBlock(node.body, null); | |
115 } | |
116 | |
117 /// Returns the innermost loop which [node] is effectively part of. | |
118 Continuation getLoopHeader(Expression exp) { | |
119 Node node = exp.parent; | |
120 while (node != null && node is! Continuation) { | |
121 node = node.parent; | |
122 } | |
123 if (node is Continuation) { | |
124 if (node.isRecursive) | |
floitsch
2015/07/17 17:19:45
multi-line ifs should have {}
asgerf
2015/07/20 09:10:56
Done.
| |
125 return node; | |
126 else | |
127 return loopTarget[node]; | |
128 } | |
129 return null; | |
130 } | |
131 | |
132 /// Returns the innermost of two loops and marks its as being | |
133 /// a subloop in the other loop. | |
134 /// | |
135 /// A loop is said to be a subloop of an enclosing loop if it can invoke | |
136 /// that loop recursively. This information is stored in [loopTarget]. | |
137 /// | |
138 /// This method is only invoked with two distinct loops if there is a | |
139 /// point that can reach a recursive invocation of both loops. | |
140 /// This implies that one loop is nested in the other, because they must | |
141 /// both be in scope at that point. | |
142 Continuation _determineInnerLoop(Continuation c1, Continuation c2) { | |
floitsch
2015/07/17 17:19:45
The main-goal of this function is to mark nesting,
asgerf
2015/07/20 09:10:56
Done.
| |
143 assert(c1 == null || c1.isRecursive); | |
144 assert(c2 == null || c2.isRecursive); | |
145 if (c1 == null) return c2; | |
146 if (c2 == null) return c1; | |
147 if (c1 == c2) return c1; | |
148 if (loopDepth[c1] > loopDepth[c2]) { | |
149 loopTarget[c1] = _determineInnerLoop(loopTarget[c1], c2); | |
150 return c1; | |
151 } else { | |
152 loopTarget[c2] = _determineInnerLoop(loopTarget[c2], c1); | |
153 return c2; | |
154 } | |
155 } | |
156 | |
157 /// Analyzes the body of [cont] and returns the innermost loop | |
158 /// that can be invoked recursively can be invoked from [cont] | |
floitsch
2015/07/17 17:19:45
-can be invoked-
asgerf
2015/07/20 09:10:56
Done.
| |
159 /// (other than [cont] itself). | |
160 /// | |
161 /// [catchLoop] is the innermost loop that can be invoked recursively | |
162 /// from the current exception handler. | |
163 Continuation _processContinuation(Continuation cont, Continuation catchLoop) { | |
164 if (cont.isRecursive) { | |
165 ++currentDepth; | |
166 loopDepth[cont] = currentDepth; | |
167 Continuation target = _processBlock(cont.body, catchLoop); | |
168 _determineInnerLoop(loopTarget[cont], target); | |
169 --currentDepth; | |
170 } else { | |
171 loopTarget[cont] = _processBlock(cont.body, catchLoop); | |
172 } | |
173 return loopTarget[cont]; | |
174 } | |
175 | |
176 bool _isCallContinuation(Continuation cont) { | |
177 return cont.hasExactlyOneUse && cont.firstRef.parent is CallExpression; | |
178 } | |
179 | |
180 /// Analyzes a basic block and returns the innermost loop that | |
181 /// can be invoked recursively from that block. | |
182 Continuation _processBlock(Expression node, Continuation catchLoop) { | |
183 List<Continuation> callContinuations = <Continuation>[]; | |
184 for (; node is! TailExpression; node = node.next) { | |
185 if (node is LetCont) { | |
186 for (Continuation cont in node.continuations) { | |
187 // Do not process call continuations at the binding site. | |
188 // We step into call continuations as part of the same basic block. | |
floitsch
2015/07/17 17:19:45
I don't understand this comment.
asgerf
2015/07/20 09:10:56
Refactored code and comment.
The "is CallExpressi
| |
189 if (!_isCallContinuation(cont)) { | |
190 _processContinuation(cont, catchLoop); | |
191 } | |
192 } | |
193 } else if (node is CallExpression) { | |
194 callContinuations.add(node.continuation.definition); | |
195 } else if (node is LetHandler) { | |
196 catchLoop = _processContinuation(node.handler, catchLoop); | |
197 } | |
198 } | |
199 Continuation target; | |
200 if (node is InvokeContinuation) { | |
201 if (node.isRecursive) { | |
202 target = node.continuation.definition; | |
203 } else { | |
204 target = loopTarget[node.continuation.definition]; | |
205 } | |
206 } else if (node is Branch) { | |
207 target = _determineInnerLoop( | |
208 loopTarget[node.trueContinuation.definition], | |
209 loopTarget[node.falseContinuation.definition]); | |
210 } else { | |
211 assert(node is Unreachable || node is Throw); | |
212 } | |
213 target = _determineInnerLoop(target, catchLoop); | |
214 for (Continuation cont in callContinuations) { | |
215 // Store the loop target on each call continuation in the basic block. | |
216 // Because we walk over call continuations as part of the basic block | |
217 // traversal, these do not get their loop target set by otherwise. | |
floitsch
2015/07/17 17:19:45
-by-
asgerf
2015/07/20 09:10:56
Done.
| |
218 loopTarget[cont] = target; | |
219 } | |
220 return target; | |
221 } | |
222 } | |
OLD | NEW |