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

Side by Side Diff: pkg/compiler/lib/src/ssa/builder.dart

Issue 2724473004: Tweak Florian's dart2js inlining heuristic change (Closed)
Patch Set: Created 3 years, 9 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) 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
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
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
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
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
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 }
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