| 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 |