Add a whitelist for functions that we always want to inline.

R=johnniwinther@google.com

Review URL: https://codereview.chromium.org//543583002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@39869 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
karlklose@google.com 2014-09-04 13:35:19 +00:00
parent 06ca446607
commit f4a8837f2d
2 changed files with 60 additions and 4 deletions

View file

@ -56,6 +56,8 @@ class JavaScriptBackend extends Backend {
static final Uri DART_JS_HELPER = new Uri(scheme: 'dart', path: '_js_helper');
static final Uri DART_INTERCEPTORS =
new Uri(scheme: 'dart', path: '_interceptors');
static final Uri DART_INTERNAL =
new Uri(scheme: 'dart', path: '_internal');
static final Uri DART_FOREIGN_HELPER =
new Uri(scheme: 'dart', path: '_foreign_helper');
static final Uri DART_JS_MIRRORS =
@ -70,6 +72,24 @@ class JavaScriptBackend extends Backend {
static const String INVOKE_ON = '_getCachedInvocation';
static const String START_ROOT_ISOLATE = 'startRootIsolate';
/// The list of functions for classes in the [internalLibrary] that we want
/// to inline always. Any function in this list must be inlinable with
/// respect to the conditions used in [InlineWeeder.canInline], except for
/// size/complexity heuristics.
static const Map<String, List<String>> ALWAYS_INLINE =
const <String, List<String>> {
'IterableMixinWorkaround': const <String>['forEach'],
};
/// List of [FunctionElement]s that we want to inline always. This list is
/// filled when resolution is complete by looking up in [internalLibrary].
List<FunctionElement> functionsToAlwaysInline;
/// Reference to the internal library to lookup functions to always inline.
LibraryElement internalLibrary;
/// Set of classes that need to be considered for reflection although not
/// otherwise visible during resolution.
Iterable<ClassElement> get classesRequiredForReflection {
@ -886,6 +906,26 @@ class JavaScriptBackend extends Backend {
super.onResolutionComplete();
computeMembersNeededForReflection();
rti.computeClassesNeedingRti();
computeFunctionsToAlwaysInline();
}
void computeFunctionsToAlwaysInline() {
functionsToAlwaysInline = <FunctionElement>[];
if (internalLibrary == null) return;
// Try to find all functions intended to always inline. If their enclosing
// class is not resolved we skip the methods, but it is an error to mention
// a function or class that cannot be found.
for (String className in ALWAYS_INLINE.keys) {
ClassElement cls = find(internalLibrary, className);
if (cls.resolutionState != STATE_DONE) continue;
for (String functionName in ALWAYS_INLINE[className]) {
Element function = cls.lookupMember(functionName);
assert(invariant(cls, function is FunctionElement,
message: 'unable to find function $functionName in $className'));
functionsToAlwaysInline.add(function);
}
}
}
void registerGetRuntimeTypeArgument(Registry registry) {
@ -1584,6 +1624,8 @@ class JavaScriptBackend extends Backend {
Uri uri = library.canonicalUri;
if (uri == DART_JS_HELPER) {
jsHelperLibrary = library;
} else if (uri == DART_INTERNAL) {
internalLibrary = library;
} else if (uri == DART_INTERCEPTORS) {
interceptorsLibrary = library;
} else if (uri == DART_FOREIGN_HELPER) {

View file

@ -1256,6 +1256,13 @@ class SsaBuilder extends ResolvedVisitor {
if (cachedCanBeInlined == true) return cachedCanBeInlined;
if (backend.functionsToAlwaysInline.contains(function)) {
// Inline this function regardless of it's size.
assert(InlineWeeder.canBeInlined(function.node, -1, false,
allowLoops: true));
return true;
}
int numParameters = function.functionSignature.parameterCount;
int maxInliningNodes;
bool useMaxInliningNodes = true;
@ -6000,6 +6007,9 @@ class StringBuilderVisitor extends ast.Visitor {
* This class visits the method that is a candidate for inlining and
* finds whether it is too difficult to inline.
*/
// TODO(karlklose): refactor to make it possible to distinguish between
// implementation restrictions (for example, we *can't* inline multiple returns)
// and heuristics (we *shouldn't* inline large functions).
class InlineWeeder extends ast.Visitor {
// Invariant: *INSIDE_LOOP* > *OUTSIDE_LOOP*
static const INLINING_NODES_OUTSIDE_LOOP = 18;
@ -6012,14 +6022,18 @@ class InlineWeeder extends ast.Visitor {
int nodeCount = 0;
final int maxInliningNodes;
final bool useMaxInliningNodes;
final bool allowLoops;
InlineWeeder(this.maxInliningNodes, this.useMaxInliningNodes);
InlineWeeder(this.maxInliningNodes,
this.useMaxInliningNodes,
this.allowLoops);
static bool canBeInlined(ast.FunctionExpression functionExpression,
int maxInliningNodes,
bool useMaxInliningNodes) {
bool useMaxInliningNodes,
{bool allowLoops: false}) {
InlineWeeder weeder =
new InlineWeeder(maxInliningNodes, useMaxInliningNodes);
new InlineWeeder(maxInliningNodes, useMaxInliningNodes, allowLoops);
weeder.visit(functionExpression.initializers);
weeder.visit(functionExpression.body);
return !weeder.tooDifficult;
@ -6067,7 +6081,7 @@ class InlineWeeder extends ast.Visitor {
// It's actually not difficult to inline a method with a loop, but
// our measurements show that it's currently better to not inline a
// method that contains a loop.
tooDifficult = true;
if (!allowLoops) tooDifficult = true;
}
void visitRedirectingFactoryBody(ast.RedirectingFactoryBody node) {