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

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

Issue 2558633007: Improve inlining heuristics. (Closed)
Patch Set: Created 4 years 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 460 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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 }
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