OLD | NEW |
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 | 9 |
10 /** | 10 /** |
(...skipping 21 matching lines...) Expand all Loading... |
32 _ReductionTask task = _worklist.removeLast(); | 32 _ReductionTask task = _worklist.removeLast(); |
33 _processTask(task); | 33 _processTask(task); |
34 } | 34 } |
35 } | 35 } |
36 | 36 |
37 /// Call instead of [_iterateWorklist] to check at every step that no | 37 /// Call instead of [_iterateWorklist] to check at every step that no |
38 /// redex was missed. | 38 /// redex was missed. |
39 void _debugWorklist(FunctionDefinition root) { | 39 void _debugWorklist(FunctionDefinition root) { |
40 while (_worklist.isNotEmpty) { | 40 while (_worklist.isNotEmpty) { |
41 _ReductionTask task = _worklist.removeLast(); | 41 _ReductionTask task = _worklist.removeLast(); |
42 String irBefore = root.debugString({ | 42 String irBefore = |
43 task.node: '${task.kind} applied here' | 43 root.debugString({task.node: '${task.kind} applied here'}); |
44 }); | |
45 _processTask(task); | 44 _processTask(task); |
46 Set seenRedexes = _worklist.where(isValidTask).toSet(); | 45 Set seenRedexes = _worklist.where(isValidTask).toSet(); |
47 Set actualRedexes = (new _RedexVisitor([])..visit(root)).worklist.toSet(); | 46 Set actualRedexes = (new _RedexVisitor([])..visit(root)).worklist.toSet(); |
48 if (!seenRedexes.containsAll(actualRedexes)) { | 47 if (!seenRedexes.containsAll(actualRedexes)) { |
49 _ReductionTask missedTask = | 48 _ReductionTask missedTask = |
50 actualRedexes.firstWhere((x) => !seenRedexes.contains(x)); | 49 actualRedexes.firstWhere((x) => !seenRedexes.contains(x)); |
51 print('\nBEFORE $task:\n'); | 50 print('\nBEFORE $task:\n'); |
52 print(irBefore); | 51 print(irBefore); |
53 print('\nAFTER $task:\n'); | 52 print('\nAFTER $task:\n'); |
54 root.debugPrint({ | 53 root.debugPrint({missedTask.node: 'MISSED ${missedTask.kind}'}); |
55 missedTask.node: 'MISSED ${missedTask.kind}' | |
56 }); | |
57 throw 'Missed $missedTask after processing $task'; | 54 throw 'Missed $missedTask after processing $task'; |
58 } | 55 } |
59 } | 56 } |
60 } | 57 } |
61 | 58 |
62 bool isValidTask(_ReductionTask task) { | 59 bool isValidTask(_ReductionTask task) { |
63 switch (task.kind) { | 60 switch (task.kind) { |
64 case _ReductionKind.DEAD_VAL: | 61 case _ReductionKind.DEAD_VAL: |
65 return _isDeadVal(task.node); | 62 return _isDeadVal(task.node); |
66 case _ReductionKind.DEAD_CONT: | 63 case _ReductionKind.DEAD_CONT: |
67 return _isDeadCont(task.node); | 64 return _isDeadCont(task.node); |
68 case _ReductionKind.BETA_CONT_LIN: | 65 case _ReductionKind.BETA_CONT_LIN: |
69 return _isBetaContLin(task.node); | 66 return _isBetaContLin(task.node); |
70 case _ReductionKind.ETA_CONT: | 67 case _ReductionKind.ETA_CONT: |
71 return _isEtaCont(task.node); | 68 return _isEtaCont(task.node); |
72 case _ReductionKind.DEAD_PARAMETER: | 69 case _ReductionKind.DEAD_PARAMETER: |
73 return _isDeadParameter(task.node); | 70 return _isDeadParameter(task.node); |
74 case _ReductionKind.BRANCH: | 71 case _ReductionKind.BRANCH: |
75 return _isBranchRedex(task.node); | 72 return _isBranchRedex(task.node); |
76 } | 73 } |
77 } | 74 } |
78 | 75 |
79 /// Removes the given node from the CPS graph, replacing it with its body | 76 /// Removes the given node from the CPS graph, replacing it with its body |
80 /// and marking it as deleted. The node's parent must be a [[InteriorNode]]. | 77 /// and marking it as deleted. The node's parent must be a [[InteriorNode]]. |
81 void _removeNode(InteriorNode node) { | 78 void _removeNode(InteriorNode node) { |
82 Node body = node.body; | 79 Node body = node.body; |
83 InteriorNode parent = node.parent; | 80 InteriorNode parent = node.parent; |
84 assert(parent.body == node); | 81 assert(parent.body == node); |
85 | 82 |
86 body.parent = parent; | 83 body.parent = parent; |
87 parent.body = body; | 84 parent.body = body; |
88 node.parent = null; | 85 node.parent = null; |
89 | 86 |
90 // The removed node could be the last node between a continuation and | 87 // The removed node could be the last node between a continuation and |
91 // an InvokeContinuation in the body. | 88 // an InvokeContinuation in the body. |
92 if (parent is Continuation) { | 89 if (parent is Continuation) { |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
284 if (condition is Constant) { | 281 if (condition is Constant) { |
285 target = isTruthyConstant(condition.value, strict: branch.isStrictCheck) | 282 target = isTruthyConstant(condition.value, strict: branch.isStrictCheck) |
286 ? branch.trueContinuation | 283 ? branch.trueContinuation |
287 : branch.falseContinuation; | 284 : branch.falseContinuation; |
288 } else if (_isBranchTargetOfUselessIf(branch.trueContinuation)) { | 285 } else if (_isBranchTargetOfUselessIf(branch.trueContinuation)) { |
289 target = branch.trueContinuation; | 286 target = branch.trueContinuation; |
290 } else { | 287 } else { |
291 return; | 288 return; |
292 } | 289 } |
293 | 290 |
294 InvokeContinuation invoke = new InvokeContinuation( | 291 InvokeContinuation invoke = new InvokeContinuation(target, <Primitive>[] |
295 target, <Primitive>[] | |
296 // TODO(sra): Add sourceInformation. | 292 // TODO(sra): Add sourceInformation. |
297 /*, sourceInformation: branch.sourceInformation*/); | 293 /*, sourceInformation: branch.sourceInformation*/); |
298 branch.parent.body = invoke; | 294 branch.parent.body = invoke; |
299 invoke.parent = branch.parent; | 295 invoke.parent = branch.parent; |
300 branch.parent = null; | 296 branch.parent = null; |
301 | 297 |
302 new _RemovalVisitor(_worklist).visit(branch); | 298 new _RemovalVisitor(_worklist).visit(branch); |
303 } | 299 } |
304 | 300 |
305 void _reduceDeadParameter(_ReductionTask task) { | 301 void _reduceDeadParameter(_ReductionTask task) { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
349 } | 345 } |
350 | 346 |
351 void _checkEtaCont(Continuation continuation) { | 347 void _checkEtaCont(Continuation continuation) { |
352 if (_isEtaCont(continuation)) { | 348 if (_isEtaCont(continuation)) { |
353 _worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, continuation)); | 349 _worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, continuation)); |
354 } | 350 } |
355 } | 351 } |
356 | 352 |
357 void _checkUselessBranchTarget(Continuation continuation) { | 353 void _checkUselessBranchTarget(Continuation continuation) { |
358 if (_isBranchTargetOfUselessIf(continuation)) { | 354 if (_isBranchTargetOfUselessIf(continuation)) { |
359 _worklist.add(new _ReductionTask(_ReductionKind.BRANCH, | 355 _worklist.add(new _ReductionTask( |
360 continuation.firstRef.parent)); | 356 _ReductionKind.BRANCH, continuation.firstRef.parent)); |
361 } | 357 } |
362 } | 358 } |
363 | 359 |
364 void _checkConstantBranchCondition(Primitive primitive) { | 360 void _checkConstantBranchCondition(Primitive primitive) { |
365 if (primitive is! Constant) return; | 361 if (primitive is! Constant) return; |
366 for (Reference ref = primitive.firstRef; ref != null; ref = ref.next) { | 362 for (Reference ref = primitive.firstRef; ref != null; ref = ref.next) { |
367 Node use = ref.parent; | 363 Node use = ref.parent; |
368 if (use is Branch) { | 364 if (use is Branch) { |
369 _worklist.add(new _ReductionTask(_ReductionKind.BRANCH, use)); | 365 _worklist.add(new _ReductionTask(_ReductionKind.BRANCH, use)); |
370 } | 366 } |
371 } | 367 } |
372 } | 368 } |
373 | 369 |
374 void _checkDeadPrimitive(Primitive primitive) { | 370 void _checkDeadPrimitive(Primitive primitive) { |
375 primitive = primitive.unrefined; | 371 primitive = primitive.unrefined; |
376 if (primitive is Parameter) { | 372 if (primitive is Parameter) { |
377 if (_isDeadParameter(primitive)) { | 373 if (_isDeadParameter(primitive)) { |
378 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, | 374 _worklist |
379 primitive)); | 375 .add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, primitive)); |
380 } | 376 } |
381 } else if (primitive.parent is LetPrim) { | 377 } else if (primitive.parent is LetPrim) { |
382 LetPrim letPrim = primitive.parent; | 378 LetPrim letPrim = primitive.parent; |
383 if (_isDeadVal(letPrim)) { | 379 if (_isDeadVal(letPrim)) { |
384 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, letPrim)); | 380 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, letPrim)); |
385 } | 381 } |
386 } | 382 } |
387 } | 383 } |
388 } | 384 } |
389 | 385 |
390 bool _isRemoved(InteriorNode node) { | 386 bool _isRemoved(InteriorNode node) { |
391 return node.parent == null; | 387 return node.parent == null; |
392 } | 388 } |
393 | 389 |
394 bool _isParameterRemoved(Parameter parameter) { | 390 bool _isParameterRemoved(Parameter parameter) { |
395 // A parameter can be removed directly or because its continuation is removed. | 391 // A parameter can be removed directly or because its continuation is removed. |
396 return parameter.parent == null || _isRemoved(parameter.parent); | 392 return parameter.parent == null || _isRemoved(parameter.parent); |
397 } | 393 } |
398 | 394 |
399 /// Returns true iff the bound primitive is unused, and has no effects | 395 /// Returns true iff the bound primitive is unused, and has no effects |
400 /// preventing it from being eliminated. | 396 /// preventing it from being eliminated. |
401 bool _isDeadVal(LetPrim node) { | 397 bool _isDeadVal(LetPrim node) { |
402 return !_isRemoved(node) && | 398 return !_isRemoved(node) && |
403 node.primitive.hasNoRefinedUses && | 399 node.primitive.hasNoRefinedUses && |
404 node.primitive.isSafeForElimination; | 400 node.primitive.isSafeForElimination; |
405 } | 401 } |
406 | 402 |
407 /// Returns true iff the continuation is unused. | 403 /// Returns true iff the continuation is unused. |
408 bool _isDeadCont(Continuation cont) { | 404 bool _isDeadCont(Continuation cont) { |
409 return !_isRemoved(cont) && | 405 return !_isRemoved(cont) && |
410 !cont.isReturnContinuation && | 406 !cont.isReturnContinuation && |
411 !cont.hasAtLeastOneUse; | 407 !cont.hasAtLeastOneUse; |
412 } | 408 } |
413 | 409 |
414 /// Returns true iff the continuation has a body (i.e., it is not the return | 410 /// Returns true iff the continuation has a body (i.e., it is not the return |
415 /// continuation), it is used exactly once, and that use is as the continuation | 411 /// continuation), it is used exactly once, and that use is as the continuation |
416 /// of a continuation invocation. | 412 /// of a continuation invocation. |
417 bool _isBetaContLin(Continuation cont) { | 413 bool _isBetaContLin(Continuation cont) { |
418 if (_isRemoved(cont)) return false; | 414 if (_isRemoved(cont)) return false; |
419 | 415 |
420 // There is a restriction on continuation eta-redexes that the body is not an | 416 // There is a restriction on continuation eta-redexes that the body is not an |
421 // invocation of the return continuation, because that leads to worse code | 417 // invocation of the return continuation, because that leads to worse code |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
534 | 530 |
535 bool _isUselessIf(Branch branch) { | 531 bool _isUselessIf(Branch branch) { |
536 Continuation trueCont = branch.trueContinuation; | 532 Continuation trueCont = branch.trueContinuation; |
537 Expression trueBody = _unfoldDeadRefinements(trueCont.body); | 533 Expression trueBody = _unfoldDeadRefinements(trueCont.body); |
538 if (trueBody is! InvokeContinuation) return false; | 534 if (trueBody is! InvokeContinuation) return false; |
539 Continuation falseCont = branch.falseContinuation; | 535 Continuation falseCont = branch.falseContinuation; |
540 Expression falseBody = _unfoldDeadRefinements(falseCont.body); | 536 Expression falseBody = _unfoldDeadRefinements(falseCont.body); |
541 if (falseBody is! InvokeContinuation) return false; | 537 if (falseBody is! InvokeContinuation) return false; |
542 InvokeContinuation trueInvoke = trueBody; | 538 InvokeContinuation trueInvoke = trueBody; |
543 InvokeContinuation falseInvoke = falseBody; | 539 InvokeContinuation falseInvoke = falseBody; |
544 if (trueInvoke.continuation != | 540 if (trueInvoke.continuation != falseInvoke.continuation) { |
545 falseInvoke.continuation) { | |
546 return false; | 541 return false; |
547 } | 542 } |
548 // Matching zero arguments should be adequate, since isomorphic true and false | 543 // Matching zero arguments should be adequate, since isomorphic true and false |
549 // invocations should result in redundant phis which are removed elsewhere. | 544 // invocations should result in redundant phis which are removed elsewhere. |
550 // | 545 // |
551 // Note that the argument lists are not necessarily the same length here, | 546 // Note that the argument lists are not necessarily the same length here, |
552 // because we could be looking for new redexes in the middle of performing a | 547 // because we could be looking for new redexes in the middle of performing a |
553 // dead parameter reduction, where some but not all of the invocations have | 548 // dead parameter reduction, where some but not all of the invocations have |
554 // been rewritten. In that case, we will find the redex (once) after both | 549 // been rewritten. In that case, we will find the redex (once) after both |
555 // of these invocations have been rewritten. | 550 // of these invocations have been rewritten. |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
600 | 595 |
601 // Continuation beta- and eta-redexes can overlap, namely when an eta-redex | 596 // Continuation beta- and eta-redexes can overlap, namely when an eta-redex |
602 // is invoked exactly once. We prioritize continuation beta-redexes over | 597 // is invoked exactly once. We prioritize continuation beta-redexes over |
603 // eta-redexes because some reductions (e.g., dead parameter elimination) | 598 // eta-redexes because some reductions (e.g., dead parameter elimination) |
604 // can destroy a continuation eta-redex. If we prioritized eta- over | 599 // can destroy a continuation eta-redex. If we prioritized eta- over |
605 // beta-redexes, this would implicitly "create" the corresponding beta-redex | 600 // beta-redexes, this would implicitly "create" the corresponding beta-redex |
606 // (in the sense that it would still apply) and the algorithm would not | 601 // (in the sense that it would still apply) and the algorithm would not |
607 // detect it. | 602 // detect it. |
608 if (_isDeadCont(node)) { | 603 if (_isDeadCont(node)) { |
609 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); | 604 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); |
610 } else if (_isBetaContLin(node)){ | 605 } else if (_isBetaContLin(node)) { |
611 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); | 606 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); |
612 } else if (_isEtaCont(node)) { | 607 } else if (_isEtaCont(node)) { |
613 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); | 608 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); |
614 } | 609 } |
615 } | 610 } |
616 | 611 |
617 void processParameter(Parameter node) { | 612 void processParameter(Parameter node) { |
618 if (_isDeadParameter(node)) { | 613 if (_isDeadParameter(node)) { |
619 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, node)); | 614 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, node)); |
620 } | 615 } |
(...skipping 27 matching lines...) Expand all Loading... |
648 reference.unlink(); | 643 reference.unlink(); |
649 | 644 |
650 if (reference.definition is Primitive) { | 645 if (reference.definition is Primitive) { |
651 Primitive primitive = reference.definition.unrefined; | 646 Primitive primitive = reference.definition.unrefined; |
652 Node parent = primitive.parent; | 647 Node parent = primitive.parent; |
653 // The parent might be the deleted sentinel, or it might be a | 648 // The parent might be the deleted sentinel, or it might be a |
654 // Continuation or FunctionDefinition if the primitive is an argument. | 649 // Continuation or FunctionDefinition if the primitive is an argument. |
655 if (parent is LetPrim && _isDeadVal(parent)) { | 650 if (parent is LetPrim && _isDeadVal(parent)) { |
656 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); | 651 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); |
657 } else if (primitive is Parameter && _isDeadParameter(primitive)) { | 652 } else if (primitive is Parameter && _isDeadParameter(primitive)) { |
658 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, | 653 worklist |
659 primitive)); | 654 .add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, primitive)); |
660 } | 655 } |
661 } else if (reference.definition is Continuation) { | 656 } else if (reference.definition is Continuation) { |
662 Continuation cont = reference.definition; | 657 Continuation cont = reference.definition; |
663 Node parent = cont.parent; | 658 Node parent = cont.parent; |
664 // The parent might be the deleted sentinel, or it might be a | 659 // The parent might be the deleted sentinel, or it might be a |
665 // Body if the continuation is the return continuation. | 660 // Body if the continuation is the return continuation. |
666 if (parent is LetCont) { | 661 if (parent is LetCont) { |
667 if (cont.isRecursive && cont.hasAtMostOneUse) { | 662 if (cont.isRecursive && cont.hasAtMostOneUse) { |
668 // Convert recursive to nonrecursive continuations. If the | 663 // Convert recursive to nonrecursive continuations. If the |
669 // continuation is still in use, it is either dead and will be | 664 // continuation is still in use, it is either dead and will be |
(...skipping 26 matching lines...) Expand all Loading... |
696 /// operator== since instantiations are used as Set elements. | 691 /// operator== since instantiations are used as Set elements. |
697 class _ReductionTask { | 692 class _ReductionTask { |
698 final _ReductionKind kind; | 693 final _ReductionKind kind; |
699 final Node node; | 694 final Node node; |
700 | 695 |
701 int get hashCode { | 696 int get hashCode { |
702 return (node.hashCode << 3) | kind.index; | 697 return (node.hashCode << 3) | kind.index; |
703 } | 698 } |
704 | 699 |
705 _ReductionTask(this.kind, this.node) { | 700 _ReductionTask(this.kind, this.node) { |
706 assert(node is Continuation || node is LetPrim || node is Parameter || | 701 assert(node is Continuation || |
707 node is Branch); | 702 node is LetPrim || |
| 703 node is Parameter || |
| 704 node is Branch); |
708 } | 705 } |
709 | 706 |
710 bool operator==(_ReductionTask that) { | 707 bool operator ==(_ReductionTask that) { |
711 return (that.kind == this.kind && that.node == this.node); | 708 return (that.kind == this.kind && that.node == this.node); |
712 } | 709 } |
713 | 710 |
714 String toString() => "$kind: $node"; | 711 String toString() => "$kind: $node"; |
715 } | 712 } |
OLD | NEW |