Chromium Code Reviews| Index: pkg/compiler/lib/src/js_backend/backend.dart |
| diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart |
| index 0723cd1beec8f57bc926737cfc713882f56527ef..3d9ffc42e197dd3fec21d356e855fd9eadd9fa01 100644 |
| --- a/pkg/compiler/lib/src/js_backend/backend.dart |
| +++ b/pkg/compiler/lib/src/js_backend/backend.dart |
| @@ -26,39 +26,77 @@ abstract class FunctionCompiler { |
| * !canInline(function, insideLoop: true) implies !canInline(function) |
| */ |
| class FunctionInlineCache { |
| - final Map<FunctionElement, bool> canBeInlined = |
| - new Map<FunctionElement, bool>(); |
| - |
| - final Map<FunctionElement, bool> canBeInlinedInsideLoop = |
| - new Map<FunctionElement, bool>(); |
| - |
| - // Returns [:true:]/[:false:] if we have a cached decision. |
| - // Returns [:null:] otherwise. |
| + static const int _mustNotInline = 0; |
|
Johnni Winther
2015/03/03 08:57:47
The logic for these tags is both complex and scatt
floitsch
2015/03/03 18:54:13
Made the logic much more explicit (and verbose).
|
| + // May-inline-in-loop means that the function may not be inlined outside loops |
| + // but may be inlined in a loop. |
| + static const int _mayInlineInLoop = 1; |
| + // The function can be inlined in a loop, but not outside. |
| + static const int _canInlineInLoop = 2; |
| + // May-inline means that we know that it can be inlined inside a loop, but |
| + // don't know about the general case yet. |
| + static const int _mayInline = 3; |
| + static const int _canInline = 4; |
| + static const int _mustInline = 5; |
| + |
| + final Map<FunctionElement, int> _cachedDecisions = |
| + new Map<FunctionElement, int>(); |
| + |
| + // Returns `true`/`false` if we have a cached decision. |
| + // Returns `null` otherwise. |
| bool canInline(FunctionElement element, {bool insideLoop}) { |
| - return insideLoop ? canBeInlinedInsideLoop[element] : canBeInlined[element]; |
| + int decision = _cachedDecisions[element]; |
| + if (decision == null) return null; |
| + if (insideLoop) { |
| + // We might be able to inline inside a loop, but don't know it yet. |
| + if (decision == _mayInlineInLoop) return null; |
| + return (decision >= _canInlineInLoop); |
| + } |
| + if (decision == _mayInline) return null; |
| + return decision >= _canInline; |
| } |
| void markAsInlinable(FunctionElement element, {bool insideLoop}) { |
| + int oldDecision = _cachedDecisions[element]; |
| + assert(oldDecision != _mustNotInline); |
| + |
| if (insideLoop) { |
| - canBeInlinedInsideLoop[element] = true; |
| + if (oldDecision == null) { |
| + // We know that it can be inlined in a loop, but don't know about the |
| + // non-loop case yet. |
| + _cachedDecisions[element] = _mayInline; |
| + } else if (oldDecision == _mayInlineInLoop) { |
| + _cachedDecisions[element] = _canInlineInLoop; |
| + } |
| } else { |
| - // If we can inline a function outside a loop then we should do it inside |
| - // a loop as well. |
| - canBeInlined[element] = true; |
| - canBeInlinedInsideLoop[element] = true; |
| + if (oldDecision == null || oldDecision <= _mayInline) { |
| + _cachedDecisions[element] = _canInline; |
| + } |
| } |
| } |
| - void markAsNonInlinable(FunctionElement element, {bool insideLoop}) { |
| - if (insideLoop == null || insideLoop) { |
| - // If we can't inline a function inside a loop, then we should not inline |
| - // it outside a loop either. |
| - canBeInlined[element] = false; |
| - canBeInlinedInsideLoop[element] = false; |
| + void markAsNonInlinable(FunctionElement element, {bool insideLoop: true}) { |
| + assert(_cachedDecisions[element] != _mustInline); |
| + |
| + if (insideLoop) { |
| + _cachedDecisions[element] = _mustNotInline; |
| } else { |
| - canBeInlined[element] = false; |
| + // We can't inline outside a loop, but we might still be allowed to do it |
| + // outside. |
| + int oldDecision = _cachedDecisions[element]; |
| + if (oldDecision == null) { |
| + _cachedDecisions[element] = _mayInlineInLoop; |
| + } else if (oldDecision == _mayInline) { |
| + // We already knew that the function could be inlined inside a loop, but |
| + // didn't have information about the non-loop case. Now we know that it |
| + // can't be inlined outside a loop. |
| + _cachedDecisions[element] = _canInlineInLoop; |
| + } |
| } |
| } |
| + |
| + void markAsMustInline(FunctionElement element) { |
| + _cachedDecisions[element] = _mustInline; |
| + } |
| } |
| class JavaScriptBackend extends Backend { |
| @@ -84,22 +122,10 @@ class JavaScriptBackend extends Backend { |
| 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>> { |
| - }; |
| - |
| String get patchVersion => USE_NEW_EMITTER ? 'new' : 'old'; |
| final Annotations annotations = new Annotations(); |
| - /// 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; |
| @@ -174,6 +200,7 @@ class JavaScriptBackend extends Backend { |
| ClassElement noSideEffectsClass; |
| ClassElement noThrowsClass; |
| ClassElement noInlineClass; |
| + ClassElement forceInlineClass; |
| ClassElement irRepresentationClass; |
| Element getInterceptorMethod; |
| @@ -961,26 +988,6 @@ 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) { |
| @@ -1869,6 +1876,7 @@ class JavaScriptBackend extends Backend { |
| noSideEffectsClass = findClass('NoSideEffects'); |
| noThrowsClass = findClass('NoThrows'); |
| noInlineClass = findClass('NoInline'); |
| + forceInlineClass = findClass('ForceInline'); |
| irRepresentationClass = findClass('IrRepresentation'); |
| getIsolateAffinityTagMarker = findMethod('getIsolateAffinityTag'); |
| @@ -2325,6 +2333,7 @@ class JavaScriptBackend extends Backend { |
| LibraryElement library = element.library; |
| if (!library.isPlatformLibrary && !library.canUseNative) return; |
| bool hasNoInline = false; |
| + bool hasForceInline = false; |
| bool hasNoThrows = false; |
| bool hasNoSideEffects = false; |
| for (MetadataAnnotation metadata in element.metadata) { |
| @@ -2332,7 +2341,15 @@ class JavaScriptBackend extends Backend { |
| if (!metadata.constant.value.isConstructedObject) continue; |
| ObjectConstantValue value = metadata.constant.value; |
| ClassElement cls = value.type.element; |
| - if (cls == noInlineClass) { |
| + if (cls == forceInlineClass) { |
| + hasForceInline = true; |
| + if (VERBOSE_OPTIMIZER_HINTS) { |
| + compiler.reportHint(element, |
| + MessageKind.GENERIC, |
| + {'text': "Must inline"}); |
| + } |
| + inlineCache.markAsMustInline(element); |
| + } else if (cls == noInlineClass) { |
| hasNoInline = true; |
| if (VERBOSE_OPTIMIZER_HINTS) { |
| compiler.reportHint(element, |
| @@ -2363,6 +2380,10 @@ class JavaScriptBackend extends Backend { |
| compiler.world.registerSideEffectsFree(element); |
| } |
| } |
| + if (hasForceInline && hasNoInline) { |
| + compiler.internalError(element, |
| + "@ForceInline() must not be used with @NoInline."); |
| + } |
| if (hasNoThrows && !hasNoInline) { |
| compiler.internalError(element, |
| "@NoThrows() should always be combined with @NoInline."); |