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

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

Issue 1601863005: Remove useless branches in shrinking_reductions. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Created 4 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 library dart2js.cps_ir.shrinking_reductions; 5 library dart2js.cps_ir.shrinking_reductions;
6 6
7 import 'cps_ir_nodes.dart'; 7 import 'cps_ir_nodes.dart';
8 import 'optimizers.dart'; 8 import 'optimizers.dart';
9 import 'cps_fragment.dart'; 9 import 'cps_fragment.dart';
10 import '../constants/values.dart' as values;
10 11
11 /** 12 /**
12 * [ShrinkingReducer] applies shrinking reductions to CPS terms as described 13 * [ShrinkingReducer] applies shrinking reductions to CPS terms as described
13 * in 'Compiling with Continuations, Continued' by Andrew Kennedy. 14 * in 'Compiling with Continuations, Continued' by Andrew Kennedy.
14 */ 15 */
15 class ShrinkingReducer extends Pass { 16 class ShrinkingReducer extends Pass {
16 String get passName => 'Shrinking reductions'; 17 String get passName => 'Shrinking reductions';
17 18
18 List<_ReductionTask> _worklist; 19 List<_ReductionTask> _worklist;
19 20
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
74 break; 75 break;
75 case _ReductionKind.BETA_CONT_LIN: 76 case _ReductionKind.BETA_CONT_LIN:
76 _reduceBetaContLin(task); 77 _reduceBetaContLin(task);
77 break; 78 break;
78 case _ReductionKind.ETA_CONT: 79 case _ReductionKind.ETA_CONT:
79 _reduceEtaCont(task); 80 _reduceEtaCont(task);
80 break; 81 break;
81 case _ReductionKind.DEAD_PARAMETER: 82 case _ReductionKind.DEAD_PARAMETER:
82 _reduceDeadParameter(task); 83 _reduceDeadParameter(task);
83 break; 84 break;
85 case _ReductionKind.BRANCH:
86 _reduceBranch(task);
87 break;
84 default: 88 default:
85 assert(false); 89 assert(false);
86 } 90 }
87 } 91 }
88 92
89 /// Applies the dead-val reduction: 93 /// Applies the dead-val reduction:
90 /// letprim x = V in E -> E (x not free in E). 94 /// letprim x = V in E -> E (x not free in E).
91 void _reduceDeadVal(_ReductionTask task) { 95 void _reduceDeadVal(_ReductionTask task) {
92 assert(_isDeadVal(task.node)); 96 assert(_isDeadVal(task.node));
93 97
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 } 188 }
185 } 189 }
186 190
187 // Replace all occurrences with the wrapped continuation. 191 // Replace all occurrences with the wrapped continuation.
188 cont.replaceUsesWith(wrappedCont); 192 cont.replaceUsesWith(wrappedCont);
189 193
190 // Perform bookkeeping on removed body and scan for new redexes. 194 // Perform bookkeeping on removed body and scan for new redexes.
191 new _RemovalVisitor(_worklist).visit(cont); 195 new _RemovalVisitor(_worklist).visit(cont);
192 } 196 }
193 197
198 void _reduceBranch(_ReductionTask task) {
199 Branch branch = task.node;
200 // Task can be added as both a useless if and a constant folding.
201 if (branch.parent == _DELETED) return;
202
203 // Replace Branch with InvokeContinuation of one of the targets. When the
204 // branch is deleted the other target becomes unreferenced and the chosen
205 // target becomes available for eta-cont and further reductions.
206 Continuation target;
207
208 Primitive condition = branch.condition.definition;
209 if (condition is Constant) {
210 values.ConstantValue value = condition.value;
211 if (value.isTrue) {
asgerf 2016/01/19 23:08:39 We have a helper for non-strict branches: isTruth
sra1 2016/01/20 01:03:36 Done.
212 target = branch.trueContinuation.definition;
213 } else if (value.isFalse) {
214 target = branch.falseContinuation.definition;
215 }
216 } else if (_isBranchTargetOfUselessIf(branch.trueContinuation.definition)) {
217 target = branch.trueContinuation.definition;
218 } else {
219 return;
220 }
221
222 InvokeContinuation invoke = new InvokeContinuation(
223 target, <Primitive>[]
224 // TODO(sra): Add sourceInformation.
225 /*, sourceInformation: branch.sourceInformation*/);
226 branch.parent.body = invoke;
227 invoke.parent = branch.parent;
228 branch.parent = _DELETED;
229
230 new _RemovalVisitor(_worklist).visit(branch);
231 }
232
194 void _reduceDeadParameter(_ReductionTask task) { 233 void _reduceDeadParameter(_ReductionTask task) {
195 // Continuation eta-reduction can destroy a dead parameter redex. For 234 // Continuation eta-reduction can destroy a dead parameter redex. For
196 // example, in the term: 235 // example, in the term:
197 // 236 //
198 // let cont k0(v0) = /* v0 is not used */ in 237 // let cont k0(v0) = /* v0 is not used */ in
199 // let cont k1(v1) = k0(v1) in 238 // let cont k1(v1) = k0(v1) in
200 // call foo () k1 239 // call foo () k1
201 // 240 //
202 // Continuation eta-reduction of k1 gives: 241 // Continuation eta-reduction of k1 gives:
203 // 242 //
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 // constant time by using union-find data structure. 403 // constant time by using union-find data structure.
365 for (int i = 0; i < cont.parameters.length; i++) { 404 for (int i = 0; i < cont.parameters.length; i++) {
366 if (invoke.arguments[i].definition != cont.parameters[i]) { 405 if (invoke.arguments[i].definition != cont.parameters[i]) {
367 return false; 406 return false;
368 } 407 }
369 } 408 }
370 409
371 return true; 410 return true;
372 } 411 }
373 412
413 bool _isBranchTargetOfUselessIf(Continuation cont) {
414 // A useless-if has an empty then and else branch, e.g. `if (cond);`.
415 //
416 // Detect T or F in
417 //
418 // let cont Join() = ...
419 // in let cont T() = Join()
420 // F() = Join()
421 // in branch condition T F
422 //
423 // TODO(sra): Detect isomorphic bodies for T and F, e.g. `cond ? 1 : 1`,
424 // regardless of where the letPrim is.
asgerf 2016/01/19 23:08:39 A constant pool would help here. But there could
sra1 2016/01/20 01:03:36 Acknowledged.
425
426 if (!cont.hasExactlyOneUse) return false;
427 if (cont.firstRef.parent is! Branch) return false;
428 Branch branch = cont.firstRef.parent;
429 Continuation trueCont = branch.trueContinuation.definition;
430 Continuation falseCont = branch.falseContinuation.definition;
431 // Are both continuations the same InvokeContinuation on a join?
432 if (trueCont.body is! InvokeContinuation) return false;
433 if (falseCont.body is! InvokeContinuation) return false;
434 InvokeContinuation trueInvoke = trueCont.body;
435 InvokeContinuation falseInvoke = falseCont.body;
436 if (trueInvoke.continuation.definition !=
437 falseInvoke.continuation.definition) {
438 return false;
439 }
440 assert(trueInvoke.arguments.length == falseInvoke.arguments.length);
441 if (trueInvoke.arguments.isNotEmpty) return false;
asgerf 2016/01/19 23:08:39 We should check the arguments for equality instead
sra1 2016/01/20 01:03:36 "cond ? x : x" is a redundant phi, so I did not se
442 return true;
443 }
444
374 bool _isDeadParameter(Parameter parameter) { 445 bool _isDeadParameter(Parameter parameter) {
375 // We cannot remove function parameters as an intraprocedural optimization. 446 // We cannot remove function parameters as an intraprocedural optimization.
376 if (parameter.parent is! Continuation || parameter.hasAtLeastOneUse) { 447 if (parameter.parent is! Continuation || parameter.hasAtLeastOneUse) {
377 return false; 448 return false;
378 } 449 }
379 450
380 // We cannot remove exception handler parameters, they have a fixed arity 451 // We cannot remove exception handler parameters, they have a fixed arity
381 // of two. 452 // of two.
382 if (parameter.parent.parent is LetHandler) { 453 if (parameter.parent.parent is LetHandler) {
383 return false; 454 return false;
(...skipping 20 matching lines...) Expand all
404 final List<_ReductionTask> worklist; 475 final List<_ReductionTask> worklist;
405 476
406 _RedexVisitor(this.worklist); 477 _RedexVisitor(this.worklist);
407 478
408 void processLetPrim(LetPrim node) { 479 void processLetPrim(LetPrim node) {
409 if (_isDeadVal(node)) { 480 if (_isDeadVal(node)) {
410 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); 481 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node));
411 } 482 }
412 } 483 }
413 484
485 void processBranch(Branch node) {
486 if (node.condition.definition is Constant) {
487 worklist.add(new _ReductionTask(_ReductionKind.BRANCH, node));
488 }
489 }
490
414 void processContinuation(Continuation node) { 491 void processContinuation(Continuation node) {
415 // While it would be nice to remove exception handlers that are provably 492 // While it would be nice to remove exception handlers that are provably
416 // unnecessary (e.g., the body cannot throw), that takes more sophisticated 493 // unnecessary (e.g., the body cannot throw), that takes more sophisticated
417 // analysis than we do in this pass. 494 // analysis than we do in this pass.
418 if (node.parent is LetHandler) return; 495 if (node.parent is LetHandler) return;
419 496
420 // Continuation beta- and eta-redexes can overlap, namely when an eta-redex 497 // Continuation beta- and eta-redexes can overlap, namely when an eta-redex
421 // is invoked exactly once. We prioritize continuation beta-redexes over 498 // is invoked exactly once. We prioritize continuation beta-redexes over
422 // eta-redexes because some reductions (e.g., dead parameter elimination) 499 // eta-redexes because some reductions (e.g., dead parameter elimination)
423 // can destroy a continuation eta-redex. If we prioritized eta- over 500 // can destroy a continuation eta-redex. If we prioritized eta- over
424 // beta-redexes, this would implicitly "create" the corresponding beta-redex 501 // beta-redexes, this would implicitly "create" the corresponding beta-redex
425 // (in the sense that it would still apply) and the algorithm would not 502 // (in the sense that it would still apply) and the algorithm would not
426 // detect it. 503 // detect it.
427 if (_isDeadCont(node)) { 504 if (_isDeadCont(node)) {
428 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); 505 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node));
429 } else if (_isBetaContLin(node)){ 506 } else if (_isBetaContLin(node)){
430 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); 507 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node));
431 } else if (_isEtaCont(node)) { 508 } else if (_isEtaCont(node)) {
432 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); 509 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node));
510 } else if (_isBranchTargetOfUselessIf(node)) {
511 worklist.add(new _ReductionTask(_ReductionKind.BRANCH,
512 node.firstRef.parent));
433 } 513 }
434 } 514 }
435 515
436 void processParameter(Parameter node) { 516 void processParameter(Parameter node) {
437 if (_isDeadParameter(node)) { 517 if (_isDeadParameter(node)) {
438 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, node)); 518 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, node));
439 } 519 }
440 } 520 }
441 } 521 }
442 522
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 if (cont.isRecursive && cont.hasAtMostOneUse) { 559 if (cont.isRecursive && cont.hasAtMostOneUse) {
480 // Convert recursive to nonrecursive continuations. If the 560 // Convert recursive to nonrecursive continuations. If the
481 // continuation is still in use, it is either dead and will be 561 // continuation is still in use, it is either dead and will be
482 // removed, or it is called nonrecursively outside its body. 562 // removed, or it is called nonrecursively outside its body.
483 cont.isRecursive = false; 563 cont.isRecursive = false;
484 } 564 }
485 if (_isDeadCont(cont)) { 565 if (_isDeadCont(cont)) {
486 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont)); 566 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont));
487 } else if (_isBetaContLin(cont)) { 567 } else if (_isBetaContLin(cont)) {
488 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, cont)); 568 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, cont));
569 } else if (_isBranchTargetOfUselessIf(cont)) {
570 worklist.add(
571 new _ReductionTask(_ReductionKind.BRANCH, cont.firstRef.parent));
489 } 572 }
490 } 573 }
491 } 574 }
492 } 575 }
493 } 576 }
494 577
495 578
496 579
497 class _ReductionKind { 580 class _ReductionKind {
498 final String name; 581 final String name;
499 final int hashCode; 582 final int hashCode;
500 583
501 const _ReductionKind(this.name, this.hashCode); 584 const _ReductionKind(this.name, this.hashCode);
502 585
503 static const _ReductionKind DEAD_VAL = const _ReductionKind('dead-val', 0); 586 static const _ReductionKind DEAD_VAL = const _ReductionKind('dead-val', 0);
504 static const _ReductionKind DEAD_CONT = const _ReductionKind('dead-cont', 1); 587 static const _ReductionKind DEAD_CONT = const _ReductionKind('dead-cont', 1);
505 static const _ReductionKind BETA_CONT_LIN = 588 static const _ReductionKind BETA_CONT_LIN =
506 const _ReductionKind('beta-cont-lin', 2); 589 const _ReductionKind('beta-cont-lin', 2);
507 static const _ReductionKind ETA_CONT = const _ReductionKind('eta-cont', 3); 590 static const _ReductionKind ETA_CONT = const _ReductionKind('eta-cont', 3);
508 static const _ReductionKind DEAD_PARAMETER = 591 static const _ReductionKind DEAD_PARAMETER =
509 const _ReductionKind('dead-parameter', 4); 592 const _ReductionKind('dead-parameter', 4);
593 static const _ReductionKind BRANCH = const _ReductionKind('branch', 5);
510 594
511 String toString() => name; 595 String toString() => name;
512 } 596 }
513 597
514 /// Represents a reduction task on the worklist. Implements both hashCode and 598 /// Represents a reduction task on the worklist. Implements both hashCode and
515 /// operator== since instantiations are used as Set elements. 599 /// operator== since instantiations are used as Set elements.
516 class _ReductionTask { 600 class _ReductionTask {
517 final _ReductionKind kind; 601 final _ReductionKind kind;
518 final Node node; 602 final Node node;
519 603
(...skipping 12 matching lines...) Expand all
532 616
533 String toString() => "$kind: $node"; 617 String toString() => "$kind: $node";
534 } 618 }
535 619
536 /// A dummy class used solely to mark nodes as deleted once they are removed 620 /// A dummy class used solely to mark nodes as deleted once they are removed
537 /// from a term. 621 /// from a term.
538 class _DeletedNode extends Node { 622 class _DeletedNode extends Node {
539 accept(_) {} 623 accept(_) {}
540 setParentPointers() {} 624 setParentPointers() {}
541 } 625 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698