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 part of js_backend; | 5 part of js_backend; |
| 6 | 6 |
| 7 const VERBOSE_OPTIMIZER_HINTS = false; | 7 const VERBOSE_OPTIMIZER_HINTS = false; |
| 8 | 8 |
| 9 const bool USE_CPS_IR = const bool.fromEnvironment("USE_CPS_IR"); | 9 const bool USE_CPS_IR = const bool.fromEnvironment("USE_CPS_IR"); |
| 10 | 10 |
| 11 class JavaScriptItemCompilationContext extends ItemCompilationContext { | 11 class JavaScriptItemCompilationContext extends ItemCompilationContext { |
| 12 final Set<HInstruction> boundsChecked = new Set<HInstruction>(); | 12 final Set<HInstruction> boundsChecked = new Set<HInstruction>(); |
| 13 final Set<HInstruction> allocatedFixedLists = new Set<HInstruction>(); | 13 final Set<HInstruction> allocatedFixedLists = new Set<HInstruction>(); |
| 14 } | 14 } |
| 15 | 15 |
| 16 abstract class FunctionCompiler { | 16 abstract class FunctionCompiler { |
| 17 /// Generates JavaScript code for `work.element`. | 17 /// Generates JavaScript code for `work.element`. |
| 18 jsAst.Fun compile(CodegenWorkItem work); | 18 jsAst.Fun compile(CodegenWorkItem work); |
| 19 | 19 |
| 20 Iterable get tasks; | 20 Iterable get tasks; |
| 21 } | 21 } |
| 22 | 22 |
| 23 /* | 23 /* |
| 24 * Invariants: | 24 * Invariants: |
| 25 * canInline(function) implies canInline(function, insideLoop:true) | 25 * canInline(function) implies canInline(function, insideLoop:true) |
| 26 * !canInline(function, insideLoop: true) implies !canInline(function) | 26 * !canInline(function, insideLoop: true) implies !canInline(function) |
| 27 * mustInline(function) implies canInline. | |
| 27 */ | 28 */ |
| 28 class FunctionInlineCache { | 29 class FunctionInlineCache { |
| 29 final Map<FunctionElement, bool> canBeInlined = | 30 static const int _mustNotInline = 0; |
| 30 new Map<FunctionElement, bool>(); | 31 // May-inline in loop means that we don't know yet. |
| 32 static const int _mayInlineInLoop = 1; | |
| 33 static const int _canInlineInLoop = 2; | |
| 34 static const int _canInline = 3; | |
| 35 static const int _mustInline = 4; | |
| 31 | 36 |
| 32 final Map<FunctionElement, bool> canBeInlinedInsideLoop = | 37 final Map<FunctionElement, int> _cachedDecisions = |
| 33 new Map<FunctionElement, bool>(); | 38 new Map<FunctionElement, int>(); |
| 34 | 39 |
| 35 // Returns [:true:]/[:false:] if we have a cached decision. | 40 // Returns `true`/`false` if we have a cached decision. |
| 36 // Returns [:null:] otherwise. | 41 // Returns `null` otherwise. |
| 37 bool canInline(FunctionElement element, {bool insideLoop}) { | 42 bool canInline(FunctionElement element, {bool insideLoop}) { |
| 38 return insideLoop ? canBeInlinedInsideLoop[element] : canBeInlined[element]; | 43 int decision = _cachedDecisions[element]; |
| 44 if (decision == null) return null; | |
| 45 if (insideLoop) { | |
| 46 // We might be able to inline inside a loop, but don't know it yet. | |
| 47 if (decision == _mayInlineInLoop) return null; | |
| 48 return (decision >= _canInlineInLoop); | |
| 49 } | |
| 50 return decision >= _canInline; | |
| 51 } | |
| 52 | |
| 53 bool mustInline(FunctionElement element) { | |
| 54 return _cachedDecisions[element] == _mustInline; | |
| 39 } | 55 } |
| 40 | 56 |
| 41 void markAsInlinable(FunctionElement element, {bool insideLoop}) { | 57 void markAsInlinable(FunctionElement element, {bool insideLoop}) { |
| 42 if (insideLoop) { | 58 int oldDecision = _cachedDecisions[element]; |
| 43 canBeInlinedInsideLoop[element] = true; | 59 assert(oldDecision != 0); |
| 44 } else { | 60 if (oldDecision == null) oldDecision = _mustNotInline; |
| 45 // If we can inline a function outside a loop then we should do it inside | 61 |
| 46 // a loop as well. | 62 int newDecision = insideLoop ? _canInlineInLoop : _canInline; |
| 47 canBeInlined[element] = true; | 63 |
| 48 canBeInlinedInsideLoop[element] = true; | 64 if (newDecision > oldDecision) { |
| 65 _cachedDecisions[element] = newDecision; | |
| 49 } | 66 } |
| 50 } | 67 } |
| 51 | 68 |
| 52 void markAsNonInlinable(FunctionElement element, {bool insideLoop}) { | 69 void markAsNonInlinable(FunctionElement element, {bool insideLoop}) { |
| 70 assert(_cachedDecisions[element] != _mustInline); | |
| 71 | |
| 53 if (insideLoop == null || insideLoop) { | 72 if (insideLoop == null || insideLoop) { |
|
Johnni Winther
2015/03/02 10:02:28
Why allow [insideLoop] to be null. Shouldn't the d
floitsch
2015/03/02 15:18:29
Done.
| |
| 54 // If we can't inline a function inside a loop, then we should not inline | 73 _cachedDecisions[element] = _mustNotInline; |
| 55 // it outside a loop either. | |
| 56 canBeInlined[element] = false; | |
| 57 canBeInlinedInsideLoop[element] = false; | |
| 58 } else { | 74 } else { |
| 59 canBeInlined[element] = false; | 75 // We can't inline outside a loop, but we might still be allowed to do it |
| 76 // outside. | |
| 77 // If the cached decision already has a value we must not change it. | |
| 78 // Otherwise mark this function as potentially inlinable inside a loop. | |
| 79 int oldDecision = _cachedDecisions[element]; | |
| 80 if (oldDecision == null) { | |
| 81 _cachedDecisions[element] = _mayInlineInLoop; | |
| 82 } | |
| 60 } | 83 } |
| 61 } | 84 } |
| 85 | |
| 86 void markAsMustInline(FunctionElement element) { | |
| 87 _cachedDecisions[element] = _mustInline; | |
| 88 } | |
| 62 } | 89 } |
| 63 | 90 |
| 64 class JavaScriptBackend extends Backend { | 91 class JavaScriptBackend extends Backend { |
| 65 static final Uri DART_JS_HELPER = new Uri(scheme: 'dart', path: '_js_helper'); | 92 static final Uri DART_JS_HELPER = new Uri(scheme: 'dart', path: '_js_helper'); |
| 66 static final Uri DART_INTERCEPTORS = | 93 static final Uri DART_INTERCEPTORS = |
| 67 new Uri(scheme: 'dart', path: '_interceptors'); | 94 new Uri(scheme: 'dart', path: '_interceptors'); |
| 68 static final Uri DART_INTERNAL = | 95 static final Uri DART_INTERNAL = |
| 69 new Uri(scheme: 'dart', path: '_internal'); | 96 new Uri(scheme: 'dart', path: '_internal'); |
| 70 static final Uri DART_FOREIGN_HELPER = | 97 static final Uri DART_FOREIGN_HELPER = |
| 71 new Uri(scheme: 'dart', path: '_foreign_helper'); | 98 new Uri(scheme: 'dart', path: '_foreign_helper'); |
| 72 static final Uri DART_JS_MIRRORS = | 99 static final Uri DART_JS_MIRRORS = |
| 73 new Uri(scheme: 'dart', path: '_js_mirrors'); | 100 new Uri(scheme: 'dart', path: '_js_mirrors'); |
| 74 static final Uri DART_JS_NAMES = | 101 static final Uri DART_JS_NAMES = |
| 75 new Uri(scheme: 'dart', path: '_js_names'); | 102 new Uri(scheme: 'dart', path: '_js_names'); |
| 76 static final Uri DART_EMBEDDED_NAMES = | 103 static final Uri DART_EMBEDDED_NAMES = |
| 77 new Uri(scheme: 'dart', path: '_js_embedded_names'); | 104 new Uri(scheme: 'dart', path: '_js_embedded_names'); |
| 78 static final Uri DART_ISOLATE_HELPER = | 105 static final Uri DART_ISOLATE_HELPER = |
| 79 new Uri(scheme: 'dart', path: '_isolate_helper'); | 106 new Uri(scheme: 'dart', path: '_isolate_helper'); |
| 80 static final Uri DART_HTML = | 107 static final Uri DART_HTML = |
| 81 new Uri(scheme: 'dart', path: 'html'); | 108 new Uri(scheme: 'dart', path: 'html'); |
| 82 | 109 |
| 83 static const String INVOKE_ON = '_getCachedInvocation'; | 110 static const String INVOKE_ON = '_getCachedInvocation'; |
| 84 static const String START_ROOT_ISOLATE = 'startRootIsolate'; | 111 static const String START_ROOT_ISOLATE = 'startRootIsolate'; |
| 85 | 112 |
| 86 | 113 |
| 87 /// The list of functions for classes in the [internalLibrary] that we want | |
| 88 /// to inline always. Any function in this list must be inlinable with | |
| 89 /// respect to the conditions used in [InlineWeeder.canInline], except for | |
| 90 /// size/complexity heuristics. | |
| 91 static const Map<String, List<String>> ALWAYS_INLINE = | |
| 92 const <String, List<String>> { | |
| 93 }; | |
| 94 | |
| 95 String get patchVersion => USE_NEW_EMITTER ? 'new' : 'old'; | 114 String get patchVersion => USE_NEW_EMITTER ? 'new' : 'old'; |
| 96 | 115 |
| 97 final Annotations annotations = new Annotations(); | 116 final Annotations annotations = new Annotations(); |
| 98 | 117 |
| 99 /// List of [FunctionElement]s that we want to inline always. This list is | |
| 100 /// filled when resolution is complete by looking up in [internalLibrary]. | |
| 101 List<FunctionElement> functionsToAlwaysInline; | |
| 102 | |
| 103 /// Reference to the internal library to lookup functions to always inline. | 118 /// Reference to the internal library to lookup functions to always inline. |
| 104 LibraryElement internalLibrary; | 119 LibraryElement internalLibrary; |
| 105 | 120 |
| 106 | 121 |
| 107 /// Set of classes that need to be considered for reflection although not | 122 /// Set of classes that need to be considered for reflection although not |
| 108 /// otherwise visible during resolution. | 123 /// otherwise visible during resolution. |
| 109 Iterable<ClassElement> get classesRequiredForReflection { | 124 Iterable<ClassElement> get classesRequiredForReflection { |
| 110 // TODO(herhut): Clean this up when classes needed for rti are tracked. | 125 // TODO(herhut): Clean this up when classes needed for rti are tracked. |
| 111 return [closureClass, jsIndexableClass]; | 126 return [closureClass, jsIndexableClass]; |
| 112 } | 127 } |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 167 ClassElement typeLiteralClass; | 182 ClassElement typeLiteralClass; |
| 168 ClassElement mapLiteralClass; | 183 ClassElement mapLiteralClass; |
| 169 ClassElement constMapLiteralClass; | 184 ClassElement constMapLiteralClass; |
| 170 ClassElement typeVariableClass; | 185 ClassElement typeVariableClass; |
| 171 ConstructorElement mapLiteralConstructor; | 186 ConstructorElement mapLiteralConstructor; |
| 172 ConstructorElement mapLiteralConstructorEmpty; | 187 ConstructorElement mapLiteralConstructorEmpty; |
| 173 | 188 |
| 174 ClassElement noSideEffectsClass; | 189 ClassElement noSideEffectsClass; |
| 175 ClassElement noThrowsClass; | 190 ClassElement noThrowsClass; |
| 176 ClassElement noInlineClass; | 191 ClassElement noInlineClass; |
| 192 ClassElement forceInlineClass; | |
| 177 ClassElement irRepresentationClass; | 193 ClassElement irRepresentationClass; |
| 178 | 194 |
| 179 Element getInterceptorMethod; | 195 Element getInterceptorMethod; |
| 180 | 196 |
| 181 ClassElement jsInvocationMirrorClass; | 197 ClassElement jsInvocationMirrorClass; |
| 182 | 198 |
| 183 /// If [true], the compiler will emit code that writes the name of the current | 199 /// If [true], the compiler will emit code that writes the name of the current |
| 184 /// method together with its class and library to the console the first time | 200 /// method together with its class and library to the console the first time |
| 185 /// the method is called. | 201 /// the method is called. |
| 186 static const bool TRACE_CALLS = false; | 202 static const bool TRACE_CALLS = false; |
| (...skipping 766 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 953 assert(traceHelper != null); | 969 assert(traceHelper != null); |
| 954 enqueueInResolution(traceHelper, registry); | 970 enqueueInResolution(traceHelper, registry); |
| 955 } | 971 } |
| 956 registerCheckedModeHelpers(registry); | 972 registerCheckedModeHelpers(registry); |
| 957 } | 973 } |
| 958 | 974 |
| 959 onResolutionComplete() { | 975 onResolutionComplete() { |
| 960 super.onResolutionComplete(); | 976 super.onResolutionComplete(); |
| 961 computeMembersNeededForReflection(); | 977 computeMembersNeededForReflection(); |
| 962 rti.computeClassesNeedingRti(); | 978 rti.computeClassesNeedingRti(); |
| 963 computeFunctionsToAlwaysInline(); | |
| 964 } | |
| 965 | |
| 966 void computeFunctionsToAlwaysInline() { | |
| 967 functionsToAlwaysInline = <FunctionElement>[]; | |
| 968 if (internalLibrary == null) return; | |
| 969 | |
| 970 // Try to find all functions intended to always inline. If their enclosing | |
| 971 // class is not resolved we skip the methods, but it is an error to mention | |
| 972 // a function or class that cannot be found. | |
| 973 for (String className in ALWAYS_INLINE.keys) { | |
| 974 ClassElement cls = find(internalLibrary, className); | |
| 975 if (cls.resolutionState != STATE_DONE) continue; | |
| 976 for (String functionName in ALWAYS_INLINE[className]) { | |
| 977 Element function = cls.lookupMember(functionName); | |
| 978 assert(invariant(cls, function is FunctionElement, | |
| 979 message: 'unable to find function $functionName in $className')); | |
| 980 functionsToAlwaysInline.add(function); | |
| 981 } | |
| 982 } | |
| 983 } | 979 } |
| 984 | 980 |
| 985 void registerGetRuntimeTypeArgument(Registry registry) { | 981 void registerGetRuntimeTypeArgument(Registry registry) { |
| 986 enqueueInResolution(getGetRuntimeTypeArgument(), registry); | 982 enqueueInResolution(getGetRuntimeTypeArgument(), registry); |
| 987 enqueueInResolution(getGetTypeArgumentByIndex(), registry); | 983 enqueueInResolution(getGetTypeArgumentByIndex(), registry); |
| 988 enqueueInResolution(getCopyTypeArguments(), registry); | 984 enqueueInResolution(getCopyTypeArguments(), registry); |
| 989 } | 985 } |
| 990 | 986 |
| 991 void registerCallMethodWithFreeTypeVariables( | 987 void registerCallMethodWithFreeTypeVariables( |
| 992 Element callMethod, | 988 Element callMethod, |
| (...skipping 868 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1861 | 1857 |
| 1862 typeLiteralClass = findClass('TypeImpl'); | 1858 typeLiteralClass = findClass('TypeImpl'); |
| 1863 constMapLiteralClass = findClass('ConstantMap'); | 1859 constMapLiteralClass = findClass('ConstantMap'); |
| 1864 typeVariableClass = findClass('TypeVariable'); | 1860 typeVariableClass = findClass('TypeVariable'); |
| 1865 | 1861 |
| 1866 jsIndexingBehaviorInterface = findClass('JavaScriptIndexingBehavior'); | 1862 jsIndexingBehaviorInterface = findClass('JavaScriptIndexingBehavior'); |
| 1867 | 1863 |
| 1868 noSideEffectsClass = findClass('NoSideEffects'); | 1864 noSideEffectsClass = findClass('NoSideEffects'); |
| 1869 noThrowsClass = findClass('NoThrows'); | 1865 noThrowsClass = findClass('NoThrows'); |
| 1870 noInlineClass = findClass('NoInline'); | 1866 noInlineClass = findClass('NoInline'); |
| 1867 forceInlineClass = findClass('ForceInline'); | |
| 1871 irRepresentationClass = findClass('IrRepresentation'); | 1868 irRepresentationClass = findClass('IrRepresentation'); |
| 1872 | 1869 |
| 1873 getIsolateAffinityTagMarker = findMethod('getIsolateAffinityTag'); | 1870 getIsolateAffinityTagMarker = findMethod('getIsolateAffinityTag'); |
| 1874 | 1871 |
| 1875 requiresPreambleMarker = findMethod('requiresPreamble'); | 1872 requiresPreambleMarker = findMethod('requiresPreamble'); |
| 1876 } else if (uri == DART_JS_MIRRORS) { | 1873 } else if (uri == DART_JS_MIRRORS) { |
| 1877 disableTreeShakingMarker = find(library, 'disableTreeShaking'); | 1874 disableTreeShakingMarker = find(library, 'disableTreeShaking'); |
| 1878 preserveMetadataMarker = find(library, 'preserveMetadata'); | 1875 preserveMetadataMarker = find(library, 'preserveMetadata'); |
| 1879 preserveUrisMarker = find(library, 'preserveUris'); | 1876 preserveUrisMarker = find(library, 'preserveUris'); |
| 1880 preserveLibraryNamesMarker = find(library, 'preserveLibraryNames'); | 1877 preserveLibraryNamesMarker = find(library, 'preserveLibraryNames'); |
| (...skipping 436 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2317 } | 2314 } |
| 2318 | 2315 |
| 2319 void onElementResolved(Element element, TreeElements elements) { | 2316 void onElementResolved(Element element, TreeElements elements) { |
| 2320 if (element.isFunction && annotations.noInline(element)) { | 2317 if (element.isFunction && annotations.noInline(element)) { |
| 2321 inlineCache.markAsNonInlinable(element); | 2318 inlineCache.markAsNonInlinable(element); |
| 2322 } | 2319 } |
| 2323 | 2320 |
| 2324 LibraryElement library = element.library; | 2321 LibraryElement library = element.library; |
| 2325 if (!library.isPlatformLibrary && !library.canUseNative) return; | 2322 if (!library.isPlatformLibrary && !library.canUseNative) return; |
| 2326 bool hasNoInline = false; | 2323 bool hasNoInline = false; |
| 2324 bool hasForceInline = false; | |
| 2327 bool hasNoThrows = false; | 2325 bool hasNoThrows = false; |
| 2328 bool hasNoSideEffects = false; | 2326 bool hasNoSideEffects = false; |
| 2329 for (MetadataAnnotation metadata in element.metadata) { | 2327 for (MetadataAnnotation metadata in element.metadata) { |
| 2330 metadata.ensureResolved(compiler); | 2328 metadata.ensureResolved(compiler); |
| 2331 if (!metadata.constant.value.isConstructedObject) continue; | 2329 if (!metadata.constant.value.isConstructedObject) continue; |
| 2332 ObjectConstantValue value = metadata.constant.value; | 2330 ObjectConstantValue value = metadata.constant.value; |
| 2333 ClassElement cls = value.type.element; | 2331 ClassElement cls = value.type.element; |
| 2334 if (cls == noInlineClass) { | 2332 if (cls == forceInlineClass) { |
| 2333 hasForceInline = true; | |
| 2334 if (VERBOSE_OPTIMIZER_HINTS) { | |
| 2335 compiler.reportHint(element, | |
| 2336 MessageKind.GENERIC, | |
| 2337 {'text': "Must inline"}); | |
| 2338 } | |
| 2339 inlineCache.markAsMustInline(element); | |
| 2340 } else if (cls == noInlineClass) { | |
| 2335 hasNoInline = true; | 2341 hasNoInline = true; |
| 2336 if (VERBOSE_OPTIMIZER_HINTS) { | 2342 if (VERBOSE_OPTIMIZER_HINTS) { |
| 2337 compiler.reportHint(element, | 2343 compiler.reportHint(element, |
| 2338 MessageKind.GENERIC, | 2344 MessageKind.GENERIC, |
| 2339 {'text': "Cannot inline"}); | 2345 {'text': "Cannot inline"}); |
| 2340 } | 2346 } |
| 2341 inlineCache.markAsNonInlinable(element); | 2347 inlineCache.markAsNonInlinable(element); |
| 2342 } else if (cls == noThrowsClass) { | 2348 } else if (cls == noThrowsClass) { |
| 2343 hasNoThrows = true; | 2349 hasNoThrows = true; |
| 2344 if (!Elements.isStaticOrTopLevelFunction(element)) { | 2350 if (!Elements.isStaticOrTopLevelFunction(element)) { |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 2355 } else if (cls == noSideEffectsClass) { | 2361 } else if (cls == noSideEffectsClass) { |
| 2356 hasNoSideEffects = true; | 2362 hasNoSideEffects = true; |
| 2357 if (VERBOSE_OPTIMIZER_HINTS) { | 2363 if (VERBOSE_OPTIMIZER_HINTS) { |
| 2358 compiler.reportHint(element, | 2364 compiler.reportHint(element, |
| 2359 MessageKind.GENERIC, | 2365 MessageKind.GENERIC, |
| 2360 {'text': "Has no side effects"}); | 2366 {'text': "Has no side effects"}); |
| 2361 } | 2367 } |
| 2362 compiler.world.registerSideEffectsFree(element); | 2368 compiler.world.registerSideEffectsFree(element); |
| 2363 } | 2369 } |
| 2364 } | 2370 } |
| 2371 if (hasForceInline && hasNoInline) { | |
| 2372 compiler.internalError(element, | |
| 2373 "@ForceInline() must not be used with @NoInline."); | |
| 2374 } | |
| 2365 if (hasNoThrows && !hasNoInline) { | 2375 if (hasNoThrows && !hasNoInline) { |
| 2366 compiler.internalError(element, | 2376 compiler.internalError(element, |
| 2367 "@NoThrows() should always be combined with @NoInline."); | 2377 "@NoThrows() should always be combined with @NoInline."); |
| 2368 } | 2378 } |
| 2369 if (hasNoSideEffects && !hasNoInline) { | 2379 if (hasNoSideEffects && !hasNoInline) { |
| 2370 compiler.internalError(element, | 2380 compiler.internalError(element, |
| 2371 "@NoSideEffects() should always be combined with @NoInline."); | 2381 "@NoSideEffects() should always be combined with @NoInline."); |
| 2372 } | 2382 } |
| 2373 if (element == invokeOnMethod) { | 2383 if (element == invokeOnMethod) { |
| 2374 compiler.enabledInvokeOn = true; | 2384 compiler.enabledInvokeOn = true; |
| (...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2705 } | 2715 } |
| 2706 } | 2716 } |
| 2707 | 2717 |
| 2708 /// Records that [constant] is used by the element behind [registry]. | 2718 /// Records that [constant] is used by the element behind [registry]. |
| 2709 class Dependency { | 2719 class Dependency { |
| 2710 final ConstantValue constant; | 2720 final ConstantValue constant; |
| 2711 final Element annotatedElement; | 2721 final Element annotatedElement; |
| 2712 | 2722 |
| 2713 const Dependency(this.constant, this.annotatedElement); | 2723 const Dependency(this.constant, this.annotatedElement); |
| 2714 } | 2724 } |
| OLD | NEW |