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 460 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 471 isReachable = false; | 471 isReachable = false; |
| 472 return false; | 472 return false; |
| 473 } | 473 } |
| 474 } | 474 } |
| 475 | 475 |
| 476 return true; | 476 return true; |
| 477 } | 477 } |
| 478 | 478 |
| 479 bool doesNotContainCode() { | 479 bool doesNotContainCode() { |
| 480 // A function with size 1 does not contain any code. | 480 // A function with size 1 does not contain any code. |
| 481 return InlineWeeder.canBeInlined(functionResolvedAst, 1, true, | 481 return InlineWeeder.canBeInlined(elements, functionResolvedAst, 1, true, |
| 482 enableUserAssertions: compiler.options.enableUserAssertions); | 482 enableUserAssertions: compiler.options.enableUserAssertions); |
| 483 } | 483 } |
| 484 | 484 |
| 485 bool reductiveHeuristic() { | 485 bool reductiveHeuristic() { |
| 486 // The call is on a path which is executed rarely, so inline only if it | 486 // The call is on a path which is executed rarely, so inline only if it |
| 487 // does not make the program larger. | 487 // does not make the program larger. |
| 488 if (isCalledOnce(element)) { | 488 if (isCalledOnce(element)) { |
| 489 return InlineWeeder.canBeInlined(functionResolvedAst, -1, false, | 489 return InlineWeeder.canBeInlined(elements, functionResolvedAst, -1, |
| 490 enableUserAssertions: compiler.options.enableUserAssertions); | 490 false, enableUserAssertions: compiler.options.enableUserAssertions); |
| 491 } | 491 } |
| 492 // TODO(sra): Measure if inlining would 'reduce' the size. One desirable | 492 // TODO(sra): Measure if inlining would 'reduce' the size. One desirable |
| 493 // case we miss by doing nothing is inlining very simple constructors | 493 // case we miss by doing nothing is inlining very simple constructors |
| 494 // where all fields are initialized with values from the arguments at this | 494 // where all fields are initialized with values from the arguments at this |
| 495 // call site. The code is slightly larger (`new Foo(1)` vs `Foo$(1)`) but | 495 // call site. The code is slightly larger (`new Foo(1)` vs `Foo$(1)`) but |
| 496 // that usually means the factory constructor is left unused and not | 496 // that usually means the factory constructor is left unused and not |
| 497 // emitted. | 497 // emitted. |
| 498 // We at least inline bodies that are empty (and thus have a size of 1). | 498 // We at least inline bodies that are empty (and thus have a size of 1). |
| 499 return doesNotContainCode(); | 499 return doesNotContainCode(); |
| 500 } | 500 } |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 517 } | 517 } |
| 518 | 518 |
| 519 // Do not inline code that is rarely executed unless it reduces size. | 519 // Do not inline code that is rarely executed unless it reduces size. |
| 520 if (inExpressionOfThrow || inLazyInitializerExpression) { | 520 if (inExpressionOfThrow || inLazyInitializerExpression) { |
| 521 return reductiveHeuristic(); | 521 return reductiveHeuristic(); |
| 522 } | 522 } |
| 523 | 523 |
| 524 if (cachedCanBeInlined == true) { | 524 if (cachedCanBeInlined == true) { |
| 525 // We may have forced the inlining of some methods. Therefore check | 525 // We may have forced the inlining of some methods. Therefore check |
| 526 // if we can inline this method regardless of size. | 526 // if we can inline this method regardless of size. |
| 527 assert(InlineWeeder.canBeInlined(functionResolvedAst, -1, false, | 527 assert(InlineWeeder.canBeInlined(elements, functionResolvedAst, -1, |
| 528 allowLoops: true, | 528 false, allowLoops: true, |
| 529 enableUserAssertions: compiler.options.enableUserAssertions)); | 529 enableUserAssertions: compiler.options.enableUserAssertions)); |
| 530 return true; | 530 return true; |
| 531 } | 531 } |
| 532 | 532 |
| 533 int numParameters = function.functionSignature.parameterCount; | 533 int numParameters = function.functionSignature.parameterCount; |
| 534 int maxInliningNodes; | 534 int maxInliningNodes; |
| 535 bool useMaxInliningNodes = true; | 535 bool useMaxInliningNodes = true; |
| 536 if (insideLoop) { | 536 if (insideLoop) { |
| 537 maxInliningNodes = InlineWeeder.INLINING_NODES_INSIDE_LOOP + | 537 maxInliningNodes = InlineWeeder.INLINING_NODES_INSIDE_LOOP + |
| 538 InlineWeeder.INLINING_NODES_INSIDE_LOOP_ARG_FACTOR * numParameters; | 538 InlineWeeder.INLINING_NODES_INSIDE_LOOP_ARG_FACTOR * numParameters; |
| 539 } else { | 539 } else { |
| 540 maxInliningNodes = InlineWeeder.INLINING_NODES_OUTSIDE_LOOP + | 540 maxInliningNodes = InlineWeeder.INLINING_NODES_OUTSIDE_LOOP + |
| 541 InlineWeeder.INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR * numParameters; | 541 InlineWeeder.INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR * numParameters; |
| 542 } | 542 } |
| 543 | 543 |
| 544 // If a method is called only once, and all the methods in the | 544 // If a method is called only once, and all the methods in the |
| 545 // inlining stack are called only once as well, we know we will | 545 // inlining stack are called only once as well, we know we will |
| 546 // save on output size by inlining this method. | 546 // save on output size by inlining this method. |
| 547 if (isCalledOnce(element)) { | 547 if (isCalledOnce(element)) { |
| 548 useMaxInliningNodes = false; | 548 useMaxInliningNodes = false; |
| 549 } | 549 } |
| 550 bool canInline; | 550 bool canInline; |
| 551 canInline = InlineWeeder.canBeInlined( | 551 canInline = InlineWeeder.canBeInlined( |
| 552 elements, | |
| 552 functionResolvedAst, maxInliningNodes, useMaxInliningNodes, | 553 functionResolvedAst, maxInliningNodes, useMaxInliningNodes, |
| 553 enableUserAssertions: compiler.options.enableUserAssertions); | 554 enableUserAssertions: compiler.options.enableUserAssertions); |
| 554 if (canInline) { | 555 if (canInline) { |
| 555 backend.inlineCache.markAsInlinable(element, insideLoop: insideLoop); | 556 backend.inlineCache.markAsInlinable(element, insideLoop: insideLoop); |
| 556 } else { | 557 } else { |
| 557 backend.inlineCache.markAsNonInlinable(element, insideLoop: insideLoop); | 558 backend.inlineCache.markAsNonInlinable(element, insideLoop: insideLoop); |
| 558 } | 559 } |
| 559 return canInline; | 560 return canInline; |
| 560 } | 561 } |
| 561 | 562 |
| (...skipping 6047 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 6609 static const INLINING_NODES_INSIDE_LOOP_ARG_FACTOR = 4; | 6610 static const INLINING_NODES_INSIDE_LOOP_ARG_FACTOR = 4; |
| 6610 | 6611 |
| 6611 bool seenReturn = false; | 6612 bool seenReturn = false; |
| 6612 bool tooDifficult = false; | 6613 bool tooDifficult = false; |
| 6613 int nodeCount = 0; | 6614 int nodeCount = 0; |
| 6614 final int maxInliningNodes; | 6615 final int maxInliningNodes; |
| 6615 final bool useMaxInliningNodes; | 6616 final bool useMaxInliningNodes; |
| 6616 final bool allowLoops; | 6617 final bool allowLoops; |
| 6617 final bool enableUserAssertions; | 6618 final bool enableUserAssertions; |
| 6618 | 6619 |
| 6619 InlineWeeder(this.maxInliningNodes, this.useMaxInliningNodes, this.allowLoops, | 6620 final TreeElements elements; |
| 6621 | |
| 6622 InlineWeeder(this.elements, | |
| 6623 this.maxInliningNodes, this.useMaxInliningNodes, this.allowLoops, | |
| 6620 this.enableUserAssertions); | 6624 this.enableUserAssertions); |
| 6621 | 6625 |
| 6622 static bool canBeInlined( | 6626 static bool canBeInlined( |
| 6627 TreeElements elements, | |
| 6623 ResolvedAst resolvedAst, int maxInliningNodes, bool useMaxInliningNodes, | 6628 ResolvedAst resolvedAst, int maxInliningNodes, bool useMaxInliningNodes, |
| 6624 {bool allowLoops: false, bool enableUserAssertions: null}) { | 6629 {bool allowLoops: false, bool enableUserAssertions: null}) { |
| 6625 assert(enableUserAssertions is bool); // Ensure we passed it. | 6630 assert(enableUserAssertions is bool); // Ensure we passed it. |
| 6626 if (resolvedAst.elements.containsTryStatement) return false; | 6631 if (resolvedAst.elements.containsTryStatement) return false; |
|
sra1
2017/02/28 22:48:28
elements does not need to be passed in, since it i
floitsch
2017/03/01 08:50:45
even better.
| |
| 6627 | 6632 |
| 6628 InlineWeeder weeder = new InlineWeeder(maxInliningNodes, | 6633 InlineWeeder weeder = new InlineWeeder(elements, |
| 6634 maxInliningNodes, | |
| 6629 useMaxInliningNodes, allowLoops, enableUserAssertions); | 6635 useMaxInliningNodes, allowLoops, enableUserAssertions); |
| 6630 ast.FunctionExpression functionExpression = resolvedAst.node; | 6636 ast.FunctionExpression functionExpression = resolvedAst.node; |
| 6637 | |
| 6631 weeder.visit(functionExpression.initializers); | 6638 weeder.visit(functionExpression.initializers); |
| 6632 weeder.visit(functionExpression.body); | 6639 weeder.visit(functionExpression.body); |
| 6633 weeder.visit(functionExpression.asyncModifier); | 6640 weeder.visit(functionExpression.asyncModifier); |
| 6634 return !weeder.tooDifficult; | 6641 return !weeder.tooDifficult; |
| 6635 } | 6642 } |
| 6636 | 6643 |
| 6637 bool registerNode() { | 6644 bool registerNode() { |
| 6638 if (!useMaxInliningNodes) return true; | 6645 if (!useMaxInliningNodes) return true; |
| 6639 if (nodeCount++ > maxInliningNodes) { | 6646 if (nodeCount++ > maxInliningNodes) { |
| 6640 tooDifficult = true; | 6647 tooDifficult = true; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 6675 if (!registerNode()) return; | 6682 if (!registerNode()) return; |
| 6676 tooDifficult = true; | 6683 tooDifficult = true; |
| 6677 } | 6684 } |
| 6678 | 6685 |
| 6679 void visitFunctionDeclaration(ast.Node node) { | 6686 void visitFunctionDeclaration(ast.Node node) { |
| 6680 if (!registerNode()) return; | 6687 if (!registerNode()) return; |
| 6681 tooDifficult = true; | 6688 tooDifficult = true; |
| 6682 } | 6689 } |
| 6683 | 6690 |
| 6684 void visitSend(ast.Send node) { | 6691 void visitSend(ast.Send node) { |
| 6692 /* | |
|
floitsch
2016/12/21 10:36:24
Enabling this, slightly increases the output size.
| |
| 6693 Element element = elements[node]; | |
| 6694 if (element != null && element.isParameter) { | |
| 6695 // Don't count as additional node, since it's likely that passing the | |
| 6696 // argument would cost us as much space as we inline. | |
|
sra1
2017/02/28 19:36:15
It will cost a temporary if it is used twice.
Can
floitsch
2017/03/01 08:50:45
That's true, unless the parameter is a constant or
| |
| 6697 return; | |
| 6698 } | |
| 6699 */ | |
| 6685 if (!registerNode()) return; | 6700 if (!registerNode()) return; |
| 6686 node.visitChildren(this); | 6701 node.visitChildren(this); |
| 6687 } | 6702 } |
| 6688 | 6703 |
| 6689 visitLoop(ast.Node node) { | 6704 visitLoop(ast.Node node) { |
| 6690 // It's actually not difficult to inline a method with a loop, but | 6705 // It's actually not difficult to inline a method with a loop, but |
| 6691 // our measurements show that it's currently better to not inline a | 6706 // our measurements show that it's currently better to not inline a |
| 6692 // method that contains a loop. | 6707 // method that contains a loop. |
| 6693 if (!allowLoops) tooDifficult = true; | 6708 if (!allowLoops) tooDifficult = true; |
| 6694 } | 6709 } |
| 6695 | 6710 |
| 6696 void visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) { | 6711 void visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) { |
| 6697 if (!registerNode()) return; | 6712 if (!registerNode()) return; |
| 6698 tooDifficult = true; | 6713 tooDifficult = true; |
| 6699 } | 6714 } |
| 6700 | 6715 |
| 6716 void visitConditional(ast.Conditional node) { | |
| 6717 visit(node.condition); | |
| 6718 if (tooDifficult) return; | |
| 6719 int oldCount = nodeCount; | |
|
sra1
2017/02/28 19:36:15
I think this code would be easier to understand if
floitsch
2017/03/01 08:50:45
Acknowledged.
| |
| 6720 visit(node.thenExpression); | |
| 6721 if (tooDifficult) return; | |
| 6722 int thenCount = nodeCount; | |
|
sra1
2017/02/28 19:36:15
int thenCount = nodeCount - commonPrefixCount
...
floitsch
2017/03/01 08:50:45
Acknowledged.
| |
| 6723 nodeCount = oldCount; | |
| 6724 visit(node.elseExpression); | |
| 6725 if (tooDifficult) return; | |
| 6726 if (node.condition.asSend() != null && | |
|
sra1
2017/02/28 19:36:15
Add a comment that this is a heuristic - a paramet
floitsch
2017/03/01 08:50:45
Acknowledged.
| |
| 6727 elements[node.condition]?.isParameter == true) { | |
| 6728 nodeCount = thenCount > nodeCount ? thenCount : nodeCount; | |
| 6729 } else { | |
| 6730 nodeCount += (thenCount - oldCount); | |
| 6731 } | |
| 6732 if (!registerNode()) return; | |
| 6733 | |
| 6734 } | |
| 6735 | |
| 6701 void visitRethrow(ast.Rethrow node) { | 6736 void visitRethrow(ast.Rethrow node) { |
| 6702 if (!registerNode()) return; | 6737 if (!registerNode()) return; |
| 6703 tooDifficult = true; | 6738 tooDifficult = true; |
| 6704 } | 6739 } |
| 6705 | 6740 |
| 6706 void visitReturn(ast.Return node) { | 6741 void visitReturn(ast.Return node) { |
| 6707 if (!registerNode()) return; | 6742 if (!registerNode()) return; |
| 6708 if (seenReturn || identical(node.beginToken.stringValue, 'native')) { | 6743 if (seenReturn || identical(node.beginToken.stringValue, 'native')) { |
| 6709 tooDifficult = true; | 6744 tooDifficult = true; |
| 6710 return; | 6745 return; |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 6751 this.oldReturnLocal, | 6786 this.oldReturnLocal, |
| 6752 this.oldReturnType, | 6787 this.oldReturnType, |
| 6753 this.oldResolvedAst, | 6788 this.oldResolvedAst, |
| 6754 this.oldStack, | 6789 this.oldStack, |
| 6755 this.oldLocalsHandler, | 6790 this.oldLocalsHandler, |
| 6756 this.inTryStatement, | 6791 this.inTryStatement, |
| 6757 this.allFunctionsCalledOnce, | 6792 this.allFunctionsCalledOnce, |
| 6758 this.oldElementInferenceResults) | 6793 this.oldElementInferenceResults) |
| 6759 : super(function); | 6794 : super(function); |
| 6760 } | 6795 } |
| OLD | NEW |