Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 import 'dart:collection'; | 5 import 'dart:collection'; |
| 6 | 6 |
| 7 import 'package:js_runtime/shared/embedded_names.dart'; | 7 import 'package:js_runtime/shared/embedded_names.dart'; |
| 8 | 8 |
| 9 import '../closure.dart'; | 9 import '../closure.dart'; |
| 10 import '../common.dart'; | 10 import '../common.dart'; |
| (...skipping 455 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 466 isReachable = false; | 466 isReachable = false; |
| 467 return false; | 467 return false; |
| 468 } | 468 } |
| 469 } | 469 } |
| 470 | 470 |
| 471 return true; | 471 return true; |
| 472 } | 472 } |
| 473 | 473 |
| 474 bool doesNotContainCode() { | 474 bool doesNotContainCode() { |
| 475 // A function with size 1 does not contain any code. | 475 // A function with size 1 does not contain any code. |
| 476 return InlineWeeder.canBeInlined(functionResolvedAst, 1, true, | 476 return InlineWeeder.canBeInlined(functionResolvedAst, 1, |
| 477 enableUserAssertions: compiler.options.enableUserAssertions); | 477 enableUserAssertions: compiler.options.enableUserAssertions); |
| 478 } | 478 } |
| 479 | 479 |
| 480 bool reductiveHeuristic() { | 480 bool reductiveHeuristic() { |
| 481 // The call is on a path which is executed rarely, so inline only if it | 481 // The call is on a path which is executed rarely, so inline only if it |
| 482 // does not make the program larger. | 482 // does not make the program larger. |
| 483 if (isCalledOnce(function)) { | 483 if (isCalledOnce(function)) { |
| 484 return InlineWeeder.canBeInlined(functionResolvedAst, -1, false, | 484 return InlineWeeder.canBeInlined(functionResolvedAst, null, |
| 485 enableUserAssertions: compiler.options.enableUserAssertions); | 485 enableUserAssertions: compiler.options.enableUserAssertions); |
| 486 } | 486 } |
| 487 // TODO(sra): Measure if inlining would 'reduce' the size. One desirable | 487 // TODO(sra): Measure if inlining would 'reduce' the size. One desirable |
| 488 // case we miss by doing nothing is inlining very simple constructors | 488 // case we miss by doing nothing is inlining very simple constructors |
| 489 // where all fields are initialized with values from the arguments at this | 489 // where all fields are initialized with values from the arguments at this |
| 490 // call site. The code is slightly larger (`new Foo(1)` vs `Foo$(1)`) but | 490 // call site. The code is slightly larger (`new Foo(1)` vs `Foo$(1)`) but |
| 491 // that usually means the factory constructor is left unused and not | 491 // that usually means the factory constructor is left unused and not |
| 492 // emitted. | 492 // emitted. |
| 493 // We at least inline bodies that are empty (and thus have a size of 1). | 493 // We at least inline bodies that are empty (and thus have a size of 1). |
| 494 return doesNotContainCode(); | 494 return doesNotContainCode(); |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 512 } | 512 } |
| 513 | 513 |
| 514 // Do not inline code that is rarely executed unless it reduces size. | 514 // Do not inline code that is rarely executed unless it reduces size. |
| 515 if (inExpressionOfThrow || inLazyInitializerExpression) { | 515 if (inExpressionOfThrow || inLazyInitializerExpression) { |
| 516 return reductiveHeuristic(); | 516 return reductiveHeuristic(); |
| 517 } | 517 } |
| 518 | 518 |
| 519 if (cachedCanBeInlined == true) { | 519 if (cachedCanBeInlined == true) { |
| 520 // We may have forced the inlining of some methods. Therefore check | 520 // We may have forced the inlining of some methods. Therefore check |
| 521 // if we can inline this method regardless of size. | 521 // if we can inline this method regardless of size. |
| 522 assert(InlineWeeder.canBeInlined(functionResolvedAst, -1, false, | 522 assert(InlineWeeder.canBeInlined(functionResolvedAst, null, |
| 523 allowLoops: true, | 523 allowLoops: true, |
| 524 enableUserAssertions: compiler.options.enableUserAssertions)); | 524 enableUserAssertions: compiler.options.enableUserAssertions)); |
| 525 return true; | 525 return true; |
| 526 } | 526 } |
| 527 | 527 |
| 528 int numParameters = function.functionSignature.parameterCount; | 528 int numParameters = function.functionSignature.parameterCount; |
| 529 int maxInliningNodes; | 529 int maxInliningNodes; |
| 530 bool useMaxInliningNodes = true; | |
| 531 if (insideLoop) { | 530 if (insideLoop) { |
| 532 maxInliningNodes = InlineWeeder.INLINING_NODES_INSIDE_LOOP + | 531 maxInliningNodes = InlineWeeder.INLINING_NODES_INSIDE_LOOP + |
| 533 InlineWeeder.INLINING_NODES_INSIDE_LOOP_ARG_FACTOR * numParameters; | 532 InlineWeeder.INLINING_NODES_INSIDE_LOOP_ARG_FACTOR * numParameters; |
| 534 } else { | 533 } else { |
| 535 maxInliningNodes = InlineWeeder.INLINING_NODES_OUTSIDE_LOOP + | 534 maxInliningNodes = InlineWeeder.INLINING_NODES_OUTSIDE_LOOP + |
| 536 InlineWeeder.INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR * numParameters; | 535 InlineWeeder.INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR * numParameters; |
| 537 } | 536 } |
| 538 | 537 |
| 539 // If a method is called only once, and all the methods in the | 538 // If a method is called only once, and all the methods in the |
| 540 // inlining stack are called only once as well, we know we will | 539 // inlining stack are called only once as well, we know we will |
| 541 // save on output size by inlining this method. | 540 // save on output size by inlining this method. |
| 542 if (isCalledOnce(function)) { | 541 if (isCalledOnce(function)) { |
| 543 useMaxInliningNodes = false; | 542 maxInliningNodes = null; |
| 544 } | 543 } |
| 545 bool canInline; | 544 bool canInline = InlineWeeder.canBeInlined( |
| 546 canInline = InlineWeeder.canBeInlined( | 545 functionResolvedAst, maxInliningNodes, |
| 547 functionResolvedAst, maxInliningNodes, useMaxInliningNodes, | |
| 548 enableUserAssertions: compiler.options.enableUserAssertions); | 546 enableUserAssertions: compiler.options.enableUserAssertions); |
| 549 if (canInline) { | 547 if (canInline) { |
| 550 backend.inlineCache.markAsInlinable(function, insideLoop: insideLoop); | 548 backend.inlineCache.markAsInlinable(function, insideLoop: insideLoop); |
| 551 } else { | 549 } else { |
| 552 backend.inlineCache | 550 backend.inlineCache |
| 553 .markAsNonInlinable(function, insideLoop: insideLoop); | 551 .markAsNonInlinable(function, insideLoop: insideLoop); |
| 554 } | 552 } |
| 555 return canInline; | 553 return canInline; |
| 556 } | 554 } |
| 557 | 555 |
| (...skipping 6022 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 6580 class InlineWeeder extends ast.Visitor { | 6578 class InlineWeeder extends ast.Visitor { |
| 6581 // Invariant: *INSIDE_LOOP* > *OUTSIDE_LOOP* | 6579 // Invariant: *INSIDE_LOOP* > *OUTSIDE_LOOP* |
| 6582 static const INLINING_NODES_OUTSIDE_LOOP = 18; | 6580 static const INLINING_NODES_OUTSIDE_LOOP = 18; |
| 6583 static const INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR = 3; | 6581 static const INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR = 3; |
| 6584 static const INLINING_NODES_INSIDE_LOOP = 42; | 6582 static const INLINING_NODES_INSIDE_LOOP = 42; |
| 6585 static const INLINING_NODES_INSIDE_LOOP_ARG_FACTOR = 4; | 6583 static const INLINING_NODES_INSIDE_LOOP_ARG_FACTOR = 4; |
| 6586 | 6584 |
| 6587 bool seenReturn = false; | 6585 bool seenReturn = false; |
| 6588 bool tooDifficult = false; | 6586 bool tooDifficult = false; |
| 6589 int nodeCount = 0; | 6587 int nodeCount = 0; |
| 6590 final int maxInliningNodes; | 6588 final int maxInliningNodes; // `null` for unbounded. |
| 6591 final bool useMaxInliningNodes; | |
| 6592 final bool allowLoops; | 6589 final bool allowLoops; |
| 6593 final bool enableUserAssertions; | 6590 final bool enableUserAssertions; |
| 6591 final TreeElements elements; | |
| 6594 | 6592 |
| 6595 InlineWeeder(this.maxInliningNodes, this.useMaxInliningNodes, this.allowLoops, | 6593 InlineWeeder._(this.elements, this.maxInliningNodes, this.allowLoops, |
| 6596 this.enableUserAssertions); | 6594 this.enableUserAssertions); |
| 6597 | 6595 |
| 6598 static bool canBeInlined( | 6596 static bool canBeInlined(ResolvedAst resolvedAst, int maxInliningNodes, |
| 6599 ResolvedAst resolvedAst, int maxInliningNodes, bool useMaxInliningNodes, | |
| 6600 {bool allowLoops: false, bool enableUserAssertions: null}) { | 6597 {bool allowLoops: false, bool enableUserAssertions: null}) { |
| 6601 assert(enableUserAssertions is bool); // Ensure we passed it. | 6598 assert(enableUserAssertions is bool); // Ensure we passed it. |
| 6602 if (resolvedAst.elements.containsTryStatement) return false; | 6599 if (resolvedAst.elements.containsTryStatement) return false; |
| 6603 | 6600 |
| 6604 InlineWeeder weeder = new InlineWeeder(maxInliningNodes, | 6601 InlineWeeder weeder = new InlineWeeder._(resolvedAst.elements, |
| 6605 useMaxInliningNodes, allowLoops, enableUserAssertions); | 6602 maxInliningNodes, allowLoops, enableUserAssertions); |
| 6606 ast.FunctionExpression functionExpression = resolvedAst.node; | 6603 ast.FunctionExpression functionExpression = resolvedAst.node; |
| 6604 | |
| 6607 weeder.visit(functionExpression.initializers); | 6605 weeder.visit(functionExpression.initializers); |
| 6608 weeder.visit(functionExpression.body); | 6606 weeder.visit(functionExpression.body); |
| 6609 weeder.visit(functionExpression.asyncModifier); | 6607 weeder.visit(functionExpression.asyncModifier); |
| 6610 return !weeder.tooDifficult; | 6608 return !weeder.tooDifficult; |
| 6611 } | 6609 } |
| 6612 | 6610 |
| 6613 bool registerNode() { | 6611 bool registerNode() { |
| 6614 if (!useMaxInliningNodes) return true; | 6612 if (maxInliningNodes == null) return true; |
| 6615 if (nodeCount++ > maxInliningNodes) { | 6613 if (nodeCount++ > maxInliningNodes) { |
| 6616 tooDifficult = true; | 6614 tooDifficult = true; |
| 6617 return false; | 6615 return false; |
| 6618 } else { | 6616 } else { |
| 6619 return true; | 6617 return true; |
| 6620 } | 6618 } |
| 6621 } | 6619 } |
| 6622 | 6620 |
| 6623 void visit(ast.Node node) { | 6621 void visit(ast.Node node) { |
| 6624 if (node != null) node.accept(this); | 6622 if (node != null) node.accept(this); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 6651 if (!registerNode()) return; | 6649 if (!registerNode()) return; |
| 6652 tooDifficult = true; | 6650 tooDifficult = true; |
| 6653 } | 6651 } |
| 6654 | 6652 |
| 6655 void visitFunctionDeclaration(ast.Node node) { | 6653 void visitFunctionDeclaration(ast.Node node) { |
| 6656 if (!registerNode()) return; | 6654 if (!registerNode()) return; |
| 6657 tooDifficult = true; | 6655 tooDifficult = true; |
| 6658 } | 6656 } |
| 6659 | 6657 |
| 6660 void visitSend(ast.Send node) { | 6658 void visitSend(ast.Send node) { |
| 6659 // TODO(sra): Investigate following, and possibly count occurrences, since | |
| 6660 // repeated references might cause a temporary to be assigned. | |
| 6661 // | |
| 6662 // Element element = elements[node]; | |
| 6663 // if (element != null && element.isParameter) { | |
| 6664 // // Don't count as additional node, since it's likely that passing | |
| 6665 // // the argument would cost us as much space as we inline. | |
| 6666 // return; | |
| 6667 // } | |
| 6661 if (!registerNode()) return; | 6668 if (!registerNode()) return; |
| 6662 node.visitChildren(this); | 6669 node.visitChildren(this); |
| 6663 } | 6670 } |
| 6664 | 6671 |
| 6665 visitLoop(ast.Node node) { | 6672 visitLoop(ast.Node node) { |
| 6666 // It's actually not difficult to inline a method with a loop, but | 6673 // It's actually not difficult to inline a method with a loop, but |
| 6667 // our measurements show that it's currently better to not inline a | 6674 // our measurements show that it's currently better to not inline a |
| 6668 // method that contains a loop. | 6675 // method that contains a loop. |
| 6669 if (!allowLoops) tooDifficult = true; | 6676 if (!allowLoops) tooDifficult = true; |
| 6670 } | 6677 } |
| 6671 | 6678 |
| 6672 void visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) { | 6679 void visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) { |
| 6673 if (!registerNode()) return; | 6680 if (!registerNode()) return; |
| 6674 tooDifficult = true; | 6681 tooDifficult = true; |
| 6675 } | 6682 } |
| 6676 | 6683 |
| 6684 void visitConditional(ast.Conditional node) { | |
| 6685 // Heuristic: In "parameter ? A : B" there is a high probability that | |
| 6686 // parameter is a constant. Assuming the parameter is constant, we can | |
|
floitsch
2017/03/01 08:50:15
In theory we could even look at the parameter. How
| |
| 6687 // compute a count that is bounded by the largest arm rather than the sum of | |
| 6688 // both arms. | |
| 6689 visit(node.condition); | |
| 6690 if (tooDifficult) return; | |
| 6691 int commonPrefixCount = nodeCount; | |
| 6692 | |
| 6693 visit(node.thenExpression); | |
| 6694 if (tooDifficult) return; | |
| 6695 int thenCount = nodeCount - commonPrefixCount; | |
| 6696 | |
| 6697 nodeCount = commonPrefixCount; | |
| 6698 visit(node.elseExpression); | |
| 6699 if (tooDifficult) return; | |
| 6700 int elseCount = nodeCount - commonPrefixCount; | |
| 6701 | |
| 6702 nodeCount = commonPrefixCount + thenCount + elseCount; | |
| 6703 if (node.condition.asSend() != null && | |
| 6704 elements[node.condition]?.isParameter == true) { | |
| 6705 nodeCount = | |
| 6706 commonPrefixCount + (thenCount > elseCount ? thenCount : elseCount); | |
| 6707 } | |
| 6708 // This is last so that [tooDifficult] is always updated. | |
| 6709 if (!registerNode()) return; | |
| 6710 } | |
| 6711 | |
| 6677 void visitRethrow(ast.Rethrow node) { | 6712 void visitRethrow(ast.Rethrow node) { |
| 6678 if (!registerNode()) return; | 6713 if (!registerNode()) return; |
| 6679 tooDifficult = true; | 6714 tooDifficult = true; |
| 6680 } | 6715 } |
| 6681 | 6716 |
| 6682 void visitReturn(ast.Return node) { | 6717 void visitReturn(ast.Return node) { |
| 6683 if (!registerNode()) return; | 6718 if (!registerNode()) return; |
| 6684 if (seenReturn || identical(node.beginToken.stringValue, 'native')) { | 6719 if (seenReturn || identical(node.beginToken.stringValue, 'native')) { |
| 6685 tooDifficult = true; | 6720 tooDifficult = true; |
| 6686 return; | 6721 return; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 6727 this.oldReturnLocal, | 6762 this.oldReturnLocal, |
| 6728 this.oldReturnType, | 6763 this.oldReturnType, |
| 6729 this.oldResolvedAst, | 6764 this.oldResolvedAst, |
| 6730 this.oldStack, | 6765 this.oldStack, |
| 6731 this.oldLocalsHandler, | 6766 this.oldLocalsHandler, |
| 6732 this.inTryStatement, | 6767 this.inTryStatement, |
| 6733 this.allFunctionsCalledOnce, | 6768 this.allFunctionsCalledOnce, |
| 6734 this.oldElementInferenceResults) | 6769 this.oldElementInferenceResults) |
| 6735 : super(function); | 6770 : super(function); |
| 6736 } | 6771 } |
| OLD | NEW |