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.let_sinking; |
6 | 6 |
7 import 'cps_ir_nodes.dart'; | 7 import 'cps_ir_nodes.dart'; |
8 import 'optimizers.dart'; | 8 import 'optimizers.dart'; |
| 9 import 'loop_hierarchy.dart'; |
9 | 10 |
10 /// Sinks single-use primitives to the use when this is safe and profitable. | 11 /// Sinks single-use primitives to the use when this is safe and profitable. |
11 /// | 12 /// |
12 /// To avoid sinking non-constant primitives into loops, this pass performs a | 13 /// To avoid sinking non-constant primitives into loops, this pass performs a |
13 /// control-flow analysis to determine the effective nesting of loops. | 14 /// control-flow analysis to determine the effective nesting of loops. |
14 /// | 15 /// |
15 /// In the example below, the value 'p' can be sunk to its use site in the | 16 /// 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 /// 'else' branch because that branch is not effectively part of a loop, |
17 /// despite being lexically nested in a recursive continuation. | 18 /// despite being lexically nested in a recursive continuation. |
18 /// | 19 /// |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 } | 110 } |
110 | 111 |
111 Expression getEnclosingExpression(Node node) { | 112 Expression getEnclosingExpression(Node node) { |
112 while (node is! Expression) { | 113 while (node is! Expression) { |
113 node = node.parent; | 114 node = node.parent; |
114 } | 115 } |
115 return node; | 116 return node; |
116 } | 117 } |
117 } | 118 } |
118 | 119 |
119 /// Determines the effective nesting of loops. | |
120 /// | |
121 /// The effective nesting of loops is different from the lexical nesting, since | |
122 /// recursive continuations can generally contain all the code following | |
123 /// after the loop in addition to the looping code itself. | |
124 /// | |
125 /// For example, the 'else' branch below is not effectively part of the loop: | |
126 /// | |
127 /// let rec kont x = | |
128 /// if (<loop condition>) | |
129 /// <loop body> | |
130 /// InvokeContinuation kont x' | |
131 /// else | |
132 /// <after loop> | |
133 /// return p.foo() | |
134 /// | |
135 /// We use the term "loop" to mean recursive continuation. | |
136 /// The `null` value is used to represent a context not part of any loop. | |
137 class LoopHierarchy { | |
138 /// Nesting depth of the given loop. | |
139 Map<Continuation, int> loopDepth = <Continuation, int>{}; | |
140 | |
141 /// The innermost loop (other than itself) that may be invoked recursively | |
142 /// as a result of invoking the given continuation. | |
143 Map<Continuation, Continuation> loopTarget = <Continuation, Continuation>{}; | |
144 | |
145 /// Current nesting depth. | |
146 int currentDepth = 0; | |
147 | |
148 /// Computes the loop hierarchy for the given function. | |
149 /// | |
150 /// Parent pointers must be computed for [node]. | |
151 LoopHierarchy(FunctionDefinition node) { | |
152 _processBlock(node.body, null); | |
153 } | |
154 | |
155 /// Returns the innermost loop which [cont] is effectively part of. | |
156 Continuation getLoopHeader(Continuation cont) { | |
157 return cont.isRecursive ? cont : loopTarget[cont]; | |
158 } | |
159 | |
160 /// Marks the innermost loop as a subloop of the other loop. | |
161 /// | |
162 /// Returns the innermost loop. | |
163 /// | |
164 /// Both continuations, [c1] and [c2] may be null (i.e. no loop). | |
165 /// | |
166 /// 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]. | |
168 /// | |
169 /// This method is only invoked with two distinct loops if there is a | |
170 /// point that can reach a recursive invocation of both loops. | |
171 /// This implies that one loop is nested in the other, because they must | |
172 /// both be in scope at that point. | |
173 Continuation _markInnerLoop(Continuation c1, Continuation c2) { | |
174 assert(c1 == null || c1.isRecursive); | |
175 assert(c2 == null || c2.isRecursive); | |
176 if (c1 == null) return c2; | |
177 if (c2 == null) return c1; | |
178 if (c1 == c2) return c1; | |
179 if (loopDepth[c1] > loopDepth[c2]) { | |
180 loopTarget[c1] = _markInnerLoop(loopTarget[c1], c2); | |
181 return c1; | |
182 } else { | |
183 loopTarget[c2] = _markInnerLoop(loopTarget[c2], c1); | |
184 return c2; | |
185 } | |
186 } | |
187 | |
188 /// Analyzes the body of [cont] and returns the innermost loop | |
189 /// that can be invoked recursively from [cont] (other than [cont] itself). | |
190 /// | |
191 /// [catchLoop] is the innermost loop that can be invoked recursively | |
192 /// from the current exception handler. | |
193 Continuation _processContinuation(Continuation cont, Continuation catchLoop) { | |
194 if (cont.isRecursive) { | |
195 ++currentDepth; | |
196 loopDepth[cont] = currentDepth; | |
197 Continuation target = _processBlock(cont.body, catchLoop); | |
198 _markInnerLoop(loopTarget[cont], target); | |
199 --currentDepth; | |
200 } else { | |
201 loopTarget[cont] = _processBlock(cont.body, catchLoop); | |
202 } | |
203 return loopTarget[cont]; | |
204 } | |
205 | |
206 bool _isCallContinuation(Continuation cont) { | |
207 return cont.hasExactlyOneUse && cont.firstRef.parent is CallExpression; | |
208 } | |
209 | |
210 /// Analyzes a basic block and returns the innermost loop that | |
211 /// can be invoked recursively from that block. | |
212 Continuation _processBlock(Expression node, Continuation catchLoop) { | |
213 List<Continuation> callContinuations = <Continuation>[]; | |
214 for (; node is! TailExpression; node = node.next) { | |
215 if (node is LetCont) { | |
216 for (Continuation cont in node.continuations) { | |
217 if (!_isCallContinuation(cont)) { | |
218 // Process non-call continuations at the binding site, so they | |
219 // their loop target is known at all use sites. | |
220 _processContinuation(cont, catchLoop); | |
221 } else { | |
222 // To avoid deep recursion, do not analyze call continuations | |
223 // recursively. This basic block traversal steps into the | |
224 // call contiunation after visiting its use site. We store the | |
225 // continuations in a list so we can set the loop target once | |
226 // it is known. | |
227 callContinuations.add(cont); | |
228 } | |
229 } | |
230 } else if (node is LetHandler) { | |
231 catchLoop = _processContinuation(node.handler, catchLoop); | |
232 } | |
233 } | |
234 Continuation target; | |
235 if (node is InvokeContinuation) { | |
236 if (node.isRecursive) { | |
237 target = node.continuation.definition; | |
238 } else { | |
239 target = loopTarget[node.continuation.definition]; | |
240 } | |
241 } else if (node is Branch) { | |
242 target = _markInnerLoop( | |
243 loopTarget[node.trueContinuation.definition], | |
244 loopTarget[node.falseContinuation.definition]); | |
245 } else { | |
246 assert(node is Unreachable || node is Throw); | |
247 } | |
248 target = _markInnerLoop(target, catchLoop); | |
249 for (Continuation cont in callContinuations) { | |
250 // 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 | |
252 // traversal, these do not get their loop target set otherwise. | |
253 loopTarget[cont] = target; | |
254 } | |
255 return target; | |
256 } | |
257 } | |
OLD | NEW |