Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(414)

Side by Side Diff: pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart

Issue 827763003: Reapply "Allow LetCont to bind multiple continuations." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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 part of dart2js.cps_ir.optimizers; 5 part of dart2js.cps_ir.optimizers;
6 6
7 /** 7 /**
8 * [ShrinkingReducer] applies shrinking reductions to CPS terms as described 8 * [ShrinkingReducer] applies shrinking reductions to CPS terms as described
9 * in 'Compiling with Continuations, Continued' by Andrew Kennedy. 9 * in 'Compiling with Continuations, Continued' by Andrew Kennedy.
10 */ 10 */
11 class ShrinkingReducer extends PassMixin { 11 class ShrinkingReducer extends PassMixin {
12 _RedexVisitor _redexVisitor;
13 Set<_ReductionTask> _worklist; 12 Set<_ReductionTask> _worklist;
14 13
15 static final _DeletedNode _DELETED = new _DeletedNode(); 14 static final _DeletedNode _DELETED = new _DeletedNode();
16 15
17 /// Applies shrinking reductions to root, mutating root in the process. 16 /// Applies shrinking reductions to root, mutating root in the process.
18 @override 17 @override
19 void rewriteExecutableDefinition(ExecutableDefinition root) { 18 void rewriteExecutableDefinition(ExecutableDefinition root) {
20 _worklist = new Set<_ReductionTask>(); 19 _worklist = new Set<_ReductionTask>();
21 _redexVisitor = new _RedexVisitor(_worklist); 20 _RedexVisitor redexVisitor = new _RedexVisitor(_worklist);
22 21
23 // Set all parent pointers. 22 // Set all parent pointers.
24 new ParentVisitor().visit(root); 23 new ParentVisitor().visit(root);
25 24
26 // Sweep over the term, collecting redexes into the worklist. 25 // Sweep over the term, collecting redexes into the worklist.
27 _redexVisitor.visit(root); 26 redexVisitor.visit(root);
28 27
29 // Process the worklist. 28 // Process the worklist.
30 while (_worklist.isNotEmpty) { 29 while (_worklist.isNotEmpty) {
31 _ReductionTask task = _worklist.first; 30 _ReductionTask task = _worklist.first;
32 _worklist.remove(task); 31 _worklist.remove(task);
33 _processTask(task); 32 _processTask(task);
34 } 33 }
35 } 34 }
36 35
37 /// Removes the given node from the CPS graph, replacing it with its body 36 /// Removes the given node from the CPS graph, replacing it with its body
38 /// and marking it as deleted. The node's parent must be a [[InteriorNode]]. 37 /// and marking it as deleted. The node's parent must be a [[InteriorNode]].
39 void _removeNode(InteriorNode node) { 38 void _removeNode(InteriorNode node) {
40 Node body = node.body; 39 Node body = node.body;
41 InteriorNode parent = node.parent; 40 InteriorNode parent = node.parent;
42 assert(parent.body == node); 41 assert(parent.body == node);
43 42
44 body.parent = parent; 43 body.parent = parent;
45 parent.body = body; 44 parent.body = body;
46 node.parent = _DELETED; 45 node.parent = _DELETED;
47 } 46 }
48 47
48 /// Remove a given continuation from the CPS graph. The LetCont itself is
49 /// removed if the given continuation is the only binding.
50 void _removeContinuation(Continuation cont) {
51 LetCont parent = cont.parent;
52 if (parent.continuations.length == 1) {
53 assert(cont.parent_index == 0);
54 _removeNode(parent);
55 } else {
56 List<Continuation> continuations = parent.continuations;
57 for (int i = cont.parent_index; i < continuations.length - 1; ++i) {
58 Continuation current = continuations[i + 1];
59 continuations[i] = current;
60 current.parent_index = i;
61 }
62 continuations.removeLast();
63 }
64 cont.parent = _DELETED;
65 }
66
49 void _processTask(_ReductionTask task) { 67 void _processTask(_ReductionTask task) {
50 // Lazily skip tasks for deleted nodes. 68 // Skip tasks for deleted nodes.
51 if (task.node.parent == _DELETED) { 69 if (task.node.parent == _DELETED) {
52 return; 70 return;
53 } 71 }
54 72
55 switch (task.kind) { 73 switch (task.kind) {
56 case _ReductionKind.DEAD_VAL: 74 case _ReductionKind.DEAD_VAL:
57 _reduceDeadVal(task); 75 _reduceDeadVal(task);
58 break; 76 break;
59 case _ReductionKind.DEAD_CONT: 77 case _ReductionKind.DEAD_CONT:
60 _reduceDeadCont(task); 78 _reduceDeadCont(task);
(...skipping 12 matching lines...) Expand all
73 /// Applies the dead-val reduction: 91 /// Applies the dead-val reduction:
74 /// letprim x = V in E -> E (x not free in E). 92 /// letprim x = V in E -> E (x not free in E).
75 void _reduceDeadVal(_ReductionTask task) { 93 void _reduceDeadVal(_ReductionTask task) {
76 assert(_isDeadVal(task.node)); 94 assert(_isDeadVal(task.node));
77 95
78 // Remove dead primitive. 96 // Remove dead primitive.
79 LetPrim letPrim = task.node;; 97 LetPrim letPrim = task.node;;
80 _removeNode(letPrim); 98 _removeNode(letPrim);
81 99
82 // Perform bookkeeping on removed body and scan for new redexes. 100 // Perform bookkeeping on removed body and scan for new redexes.
83 new _RemovalRedexVisitor(_worklist).visit(letPrim.primitive); 101 new _RemovalVisitor(_worklist).visit(letPrim.primitive);
84 } 102 }
85 103
86 /// Applies the dead-cont reduction: 104 /// Applies the dead-cont reduction:
87 /// letcont k x = E0 in E1 -> E1 (k not free in E1). 105 /// letcont k x = E0 in E1 -> E1 (k not free in E1).
88 void _reduceDeadCont(_ReductionTask task) { 106 void _reduceDeadCont(_ReductionTask task) {
89 assert(_isDeadCont(task.node)); 107 assert(_isDeadCont(task.node));
90 108
91 // Remove dead continuation. 109 // Remove dead continuation.
92 LetCont letCont = task.node; 110 Continuation cont = task.node;
93 _removeNode(letCont); 111 _removeContinuation(cont);
94 112
95 // Perform bookkeeping on removed body and scan for new redexes. 113 // Perform bookkeeping on removed body and scan for new redexes.
96 new _RemovalRedexVisitor(_worklist).visit(letCont.continuation); 114 new _RemovalVisitor(_worklist).visit(cont);
97 } 115 }
98 116
99 /// Applies the beta-cont-lin reduction: 117 /// Applies the beta-cont-lin reduction:
100 /// letcont k x = E0 in E1[k y] -> E1[E0[y/x]] (k not free in E1). 118 /// letcont k x = E0 in E1[k y] -> E1[E0[y/x]] (k not free in E1).
101 void _reduceBetaContLin(_ReductionTask task) { 119 void _reduceBetaContLin(_ReductionTask task) {
102 // Might have been mutated, recheck if reduction is still valid. 120 // Might have been mutated, recheck if reduction is still valid.
103 // In the following example, the beta-cont-lin reduction of k0 could have 121 // In the following example, the beta-cont-lin reduction of k0 could have
104 // been invalidated by removal of the dead continuation k1: 122 // been invalidated by removal of the dead continuation k1:
105 // 123 //
106 // letcont k0 x0 = E0 in 124 // letcont k0 x0 = E0 in
107 // letcont k1 x1 = k0 x1 in 125 // letcont k1 x1 = k0 x1 in
108 // return x2 126 // return x2
109 if (!_isBetaContLin(task.node)) { 127 if (!_isBetaContLin(task.node)) {
110 return; 128 return;
111 } 129 }
112 130
113 // Remove the continuation. 131 // Remove the continuation.
114 LetCont letCont = task.node; 132 Continuation cont = task.node;
115 Continuation cont = letCont.continuation; 133 _removeContinuation(cont);
116 _removeNode(letCont);
117 134
118 // Replace its invocation with the continuation body. 135 // Replace its invocation with the continuation body.
119 InvokeContinuation invoke = cont.firstRef.parent; 136 InvokeContinuation invoke = cont.firstRef.parent;
120 InteriorNode invokeParent = invoke.parent; 137 InteriorNode invokeParent = invoke.parent;
121 138
122 cont.body.parent = invokeParent; 139 cont.body.parent = invokeParent;
123 invokeParent.body = cont.body; 140 invokeParent.body = cont.body;
124 141
125 // Substitute the invocation argument for the continuation parameter. 142 // Substitute the invocation argument for the continuation parameter.
126 for (int i = 0; i < invoke.arguments.length; i++) { 143 for (int i = 0; i < invoke.arguments.length; i++) {
127 Reference argRef = invoke.arguments[i]; 144 Reference argRef = invoke.arguments[i];
128 argRef.definition.substituteFor(cont.parameters[i]); 145 argRef.definition.substituteFor(cont.parameters[i]);
129 } 146 }
130 147
131 // Perform bookkeeping on removed body and scan for new redexes. 148 // Perform bookkeeping on substituted body and scan for new redexes.
132 new _RemovalRedexVisitor(_worklist).visit(invoke); 149 new _RemovalVisitor(_worklist).visit(invoke);
133 } 150 }
134 151
135 /// Applies the eta-cont reduction: 152 /// Applies the eta-cont reduction:
136 /// letcont k x = j x in E -> E[j/k]. 153 /// letcont k x = j x in E -> E[j/k].
137 /// If k is unused, degenerates to dead-cont. 154 /// If k is unused, degenerates to dead-cont.
138 void _reduceEtaCont(_ReductionTask task) { 155 void _reduceEtaCont(_ReductionTask task) {
139 // Might have been mutated, recheck if reduction is still valid. 156 // Might have been mutated, recheck if reduction is still valid.
140 // In the following example, the eta-cont reduction of k1 could have been 157 // In the following example, the eta-cont reduction of k1 could have been
141 // invalidated by an earlier beta-cont-lin reduction of k0. 158 // invalidated by an earlier beta-cont-lin reduction of k0.
142 // 159 //
143 // letcont k0 x0 = E0 in 160 // letcont k0 x0 = E0 in
144 // letcont k1 x1 = k0 x1 in E1 161 // letcont k1 x1 = k0 x1 in E1
145 if (!_isEtaCont(task.node)) { 162 if (!_isEtaCont(task.node)) {
146 return; 163 return;
147 } 164 }
148 165
149 // Remove the continuation. 166 // Remove the continuation.
150 LetCont letCont = task.node; 167 Continuation cont = task.node;
151 Continuation cont = letCont.continuation; 168 _removeContinuation(cont);
152 _removeNode(letCont);
153 169
154 InvokeContinuation invoke = cont.body; 170 InvokeContinuation invoke = cont.body;
155 Continuation wrappedCont = invoke.continuation.definition; 171 Continuation wrappedCont = invoke.continuation.definition;
156 172
157 // Replace all occurrences with the wrapped continuation. 173 // Replace all occurrences with the wrapped continuation.
158 wrappedCont.substituteFor(cont); 174 wrappedCont.substituteFor(cont);
159 175
160 // Perform bookkeeping on removed body and scan for new redexes. 176 // Perform bookkeeping on removed body and scan for new redexes.
161 new _RemovalRedexVisitor(_worklist).visit(cont); 177 new _RemovalVisitor(_worklist).visit(cont);
162 } 178 }
163 } 179 }
164 180
165 /// Returns true iff the bound primitive is unused. 181 /// Returns true iff the bound primitive is unused.
166 bool _isDeadVal(LetPrim node) => !node.primitive.hasAtLeastOneUse; 182 bool _isDeadVal(LetPrim node) => !node.primitive.hasAtLeastOneUse;
167 183
168 /// Returns true iff the bound continuation is unused. 184 /// Returns true iff the continuation is unused.
169 bool _isDeadCont(LetCont node) => !node.continuation.hasAtLeastOneUse; 185 bool _isDeadCont(Continuation cont) {
186 assert(!cont.isReturnContinuation);
187 return !cont.hasAtLeastOneUse;
188 }
170 189
171 /// Returns true iff the bound continuation is used exactly once, and that 190 /// Returns true iff the continuation is used exactly once, and that
172 /// use is as the receiver of a continuation invocation. 191 /// use is as the continuation of a continuation invocation.
173 bool _isBetaContLin(LetCont node) { 192 bool _isBetaContLin(Continuation cont) {
174 Continuation cont = node.continuation;
175 if (!cont.hasExactlyOneUse) { 193 if (!cont.hasExactlyOneUse) {
176 return false; 194 return false;
177 } 195 }
178 196
179 if (cont.firstRef.parent is InvokeContinuation) { 197 if (cont.firstRef.parent is InvokeContinuation) {
180 InvokeContinuation invoke = cont.firstRef.parent; 198 InvokeContinuation invoke = cont.firstRef.parent;
181 return (cont == invoke.continuation.definition); 199 return (cont == invoke.continuation.definition);
182 } 200 }
183 201
184 return false; 202 return false;
185
186 } 203 }
187 204
188 /// Returns true iff the bound continuation consists of a continuation 205 /// Returns true iff the continuation consists of a continuation
189 /// invocation, passing on all parameters. Special cases exist (see below). 206 /// invocation, passing on all parameters. Special cases exist (see below).
190 bool _isEtaCont(LetCont node) { 207 bool _isEtaCont(Continuation cont) {
191 Continuation cont = node.continuation; 208 if (cont.body is! InvokeContinuation) {
192 if (!(cont.body is InvokeContinuation)) {
193 return false; 209 return false;
194 } 210 }
195 211
196 InvokeContinuation invoke = cont.body; 212 InvokeContinuation invoke = cont.body;
197 Continuation invokedCont = invoke.continuation.definition; 213 Continuation invokedCont = invoke.continuation.definition;
198 214
199 // Do not eta-reduce return join-points since the resulting code is worse 215 // Do not eta-reduce return join-points since the resulting code is worse
200 // in the common case (i.e. returns are moved inside `if` branches). 216 // in the common case (i.e. returns are moved inside `if` branches).
201 if (invokedCont.isReturnContinuation) { 217 if (invokedCont.isReturnContinuation) {
202 return false; 218 return false;
(...skipping 22 matching lines...) Expand all
225 return true; 241 return true;
226 } 242 }
227 243
228 /// Traverses a term and adds any found redexes to the worklist. 244 /// Traverses a term and adds any found redexes to the worklist.
229 class _RedexVisitor extends RecursiveVisitor { 245 class _RedexVisitor extends RecursiveVisitor {
230 final Set<_ReductionTask> worklist; 246 final Set<_ReductionTask> worklist;
231 247
232 _RedexVisitor(this.worklist); 248 _RedexVisitor(this.worklist);
233 249
234 void processLetPrim(LetPrim node) { 250 void processLetPrim(LetPrim node) {
235 if (node.parent == ShrinkingReducer._DELETED) { 251 if (_isDeadVal(node)) {
236 return;
237 } else if (_isDeadVal(node)) {
238 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); 252 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node));
239 } 253 }
240 } 254 }
241 255
242 void processLetCont(LetCont node) { 256 void processContinuation(Continuation node) {
243 if (node.parent == ShrinkingReducer._DELETED) { 257 if (_isDeadCont(node)) {
244 return;
245 } else if (_isDeadCont(node)) {
246 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); 258 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node));
247 } else if (_isEtaCont(node)) { 259 } else if (_isEtaCont(node)) {
248 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); 260 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node));
249 } else if (_isBetaContLin(node)){ 261 } else if (_isBetaContLin(node)){
250 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); 262 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node));
251 } 263 }
252 } 264 }
253 } 265 }
254 266
255 /// Traverses a deleted CPS term, marking existing tasks associated with a node 267 /// Traverses a deleted CPS term, marking nodes that might participate in a
256 /// within the term as deleted (which causes them to be skipped lazily when 268 /// redex as deleted and adding newly created redexes to the worklist.
257 /// popped from the worklist), and adding newly created redexes to the worklist. 269 ///
258 class _RemovalRedexVisitor extends _RedexVisitor { 270 /// Deleted nodes that might participate in a reduction task are marked so that
259 _RemovalRedexVisitor(Set<_ReductionTask> worklist) : super(worklist); 271 /// any corresponding tasks can be skipped. Nodes are marked so by setting
272 /// their parent to the deleted sentinel.
273 class _RemovalVisitor extends RecursiveVisitor {
274 final Set<_ReductionTask> worklist;
275
276 _RemovalVisitor(this.worklist);
260 277
261 void processLetPrim(LetPrim node) { 278 void processLetPrim(LetPrim node) {
262 node.parent = ShrinkingReducer._DELETED; 279 node.parent = ShrinkingReducer._DELETED;
263 } 280 }
264 281
265 void processLetCont(LetCont node) { 282 void processContinuation(Continuation node) {
266 node.parent = ShrinkingReducer._DELETED; 283 node.parent = ShrinkingReducer._DELETED;
267 } 284 }
268 285
269 void processReference(Reference reference) { 286 void processReference(Reference reference) {
270 reference.unlink(); 287 reference.unlink();
271 288
272 if (reference.definition is Primitive) { 289 if (reference.definition is Primitive) {
273 Primitive primitive = reference.definition; 290 Primitive primitive = reference.definition;
274 Node parent = primitive.parent; 291 Node parent = primitive.parent;
292 // The parent might be the deleted sentinel, or it might be a
293 // Continuation or FunctionDefinition if the primitive is an argument.
275 if (parent is LetPrim && _isDeadVal(parent)) { 294 if (parent is LetPrim && _isDeadVal(parent)) {
276 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); 295 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent));
277 } 296 }
278 } else if (reference.definition is Continuation) { 297 } else if (reference.definition is Continuation) {
279 Continuation cont = reference.definition; 298 Continuation cont = reference.definition;
280 if (cont.isRecursive && cont.hasAtMostOneUse) {
281 // Convert recursive to nonrecursive continuations.
282 // If the continuation is still in use, it is either dead and will be
283 // removed, or it is called nonrecursively outside its body.
284 cont.isRecursive = false;
285 }
286 Node parent = cont.parent; 299 Node parent = cont.parent;
287 if (parent is LetCont && _isDeadCont(parent)) { 300 // The parent might be the deleted sentinel, or it might be a
288 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, parent)); 301 // FunctionDefinition if the continuation is the return continuation.
302 if (parent is LetCont) {
303 if (cont.isRecursive && cont.hasAtMostOneUse) {
304 // Convert recursive to nonrecursive continuations. If the
305 // continuation is still in use, it is either dead and will be
306 // removed, or it is called nonrecursively outside its body.
307 cont.isRecursive = false;
308 }
309 if (_isDeadCont(cont)) {
310 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont));
311 }
289 } 312 }
290 } 313 }
291 } 314 }
292 } 315 }
293 316
294 /// Traverses the CPS term and sets node.parent for each visited node. 317 /// Traverses the CPS term and sets node.parent for each visited node.
295 class ParentVisitor extends RecursiveVisitor { 318 class ParentVisitor extends RecursiveVisitor {
296
297 processFunctionDefinition(FunctionDefinition node) { 319 processFunctionDefinition(FunctionDefinition node) {
298 node.body.parent = node; 320 node.body.parent = node;
299 node.parameters.forEach((Definition p) => p.parent = node); 321 node.parameters.forEach((Definition p) => p.parent = node);
300 } 322 }
301 323
302 processRunnableBody(RunnableBody node) { 324 processRunnableBody(RunnableBody node) {
303 node.body.parent = node; 325 node.body.parent = node;
304 } 326 }
305 327
306 processConstructorDefinition(ConstructorDefinition node) { 328 processConstructorDefinition(ConstructorDefinition node) {
(...skipping 12 matching lines...) Expand all
319 node.arguments.forEach( 341 node.arguments.forEach(
320 (RunnableBody argument) => argument.body.parent = node); 342 (RunnableBody argument) => argument.body.parent = node);
321 } 343 }
322 344
323 processLetPrim(LetPrim node) { 345 processLetPrim(LetPrim node) {
324 node.primitive.parent = node; 346 node.primitive.parent = node;
325 node.body.parent = node; 347 node.body.parent = node;
326 } 348 }
327 349
328 processLetCont(LetCont node) { 350 processLetCont(LetCont node) {
329 node.continuation.parent = node; 351 for (int i = 0; i < node.continuations.length; ++i) {
352 Continuation cont = node.continuations[i];
353 cont.parent = node;
354 cont.parent_index = i;
355 }
330 node.body.parent = node; 356 node.body.parent = node;
331 } 357 }
332 358
333 processInvokeStatic(InvokeStatic node) { 359 processInvokeStatic(InvokeStatic node) {
360 node.arguments.forEach((Reference ref) => ref.parent = node);
334 node.continuation.parent = node; 361 node.continuation.parent = node;
335 node.arguments.forEach((Reference ref) => ref.parent = node);
336 } 362 }
337 363
338 processInvokeContinuation(InvokeContinuation node) { 364 processInvokeContinuation(InvokeContinuation node) {
339 node.continuation.parent = node; 365 node.continuation.parent = node;
340 node.arguments.forEach((Reference ref) => ref.parent = node); 366 node.arguments.forEach((Reference ref) => ref.parent = node);
341 } 367 }
342 368
343 processInvokeMethod(InvokeMethod node) { 369 processInvokeMethod(InvokeMethod node) {
344 node.receiver.parent = node; 370 node.receiver.parent = node;
345 node.continuation.parent = node; 371 node.continuation.parent = node;
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 class _ReductionTask { 485 class _ReductionTask {
460 final _ReductionKind kind; 486 final _ReductionKind kind;
461 final Node node; 487 final Node node;
462 488
463 int get hashCode { 489 int get hashCode {
464 assert(kind.hashCode < (1 << 2)); 490 assert(kind.hashCode < (1 << 2));
465 return (node.hashCode << 2) | kind.hashCode; 491 return (node.hashCode << 2) | kind.hashCode;
466 } 492 }
467 493
468 _ReductionTask(this.kind, this.node) { 494 _ReductionTask(this.kind, this.node) {
469 // If new node types are added, they must be marked as deleted in 495 assert(node is Continuation || node is LetPrim);
470 // [[_RemovalRedexVisitor]].
471 assert(node is LetCont || node is LetPrim);
472 } 496 }
473 497
474 bool operator==(_ReductionTask that) { 498 bool operator==(_ReductionTask that) {
475 return (that.kind == this.kind && that.node == this.node); 499 return (that.kind == this.kind && that.node == this.node);
476 } 500 }
477 501
478 String toString() => "$kind: $node"; 502 String toString() => "$kind: $node";
479 } 503 }
480 504
481 /// A dummy class used solely to mark nodes as deleted once they are removed 505 /// A dummy class used solely to mark nodes as deleted once they are removed
482 /// from a term. 506 /// from a term.
483 class _DeletedNode extends Node { 507 class _DeletedNode extends Node {
484 accept(_) => null; 508 accept(_) => null;
485 } 509 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/redundant_phi.dart ('k') | pkg/compiler/lib/src/cps_ir/type_propagation.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698