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

Unified Diff: pkg/compiler/lib/src/js_emitter/metadata_collector.dart

Issue 1153243003: dart2js: Use frequency of occurence to sort metadata indices. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Addressed sra's comments Created 5 years, 6 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/compiler/lib/src/js_emitter/js_emitter.dart ('k') | pkg/compiler/lib/src/js_emitter/model.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/js_emitter/metadata_collector.dart
diff --git a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
index b2f1351fd9a55ba3574fc4e2725f3fcf0ce7bc75..a2eeb986d358677f708289955645f8508b4a7ba2 100644
--- a/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
+++ b/pkg/compiler/lib/src/js_emitter/metadata_collector.dart
@@ -4,27 +4,144 @@
part of dart2js.js_emitter;
+/// Represents an entry's position in one of the global metadata arrays.
+///
+/// [_rc] is used to count the number of references of the token in the
+/// ast for a program.
+/// [value] is the actual position, once they have been finalized.
+abstract class _MetadataEntry extends jsAst.DeferredNumber
+ implements Comparable {
+ jsAst.Expression get entry;
+ int get value;
+ int get _rc;
+
+ // Mark this entry as seen. On the first time this is seen, the visitor
+ // will be applied to the [entry] to also mark potential [_MetadataEntry]
+ // instances in the [entry] as seen.
+ markSeen(jsAst.BaseVisitor visitor);
+}
+
+class _BoundMetadataEntry extends _MetadataEntry {
+ int _value = -1;
+ int _rc = 0;
+ final jsAst.Expression entry;
+
+ _BoundMetadataEntry(this.entry);
+
+ bool get isFinalized => _value != -1;
+
+ finalize(int value) {
+ assert(!isFinalized);
+ _value = value;
+ }
+
+ int get value {
+ assert(isFinalized);
+ return _value;
+ }
+
+ bool get isUsed => _rc > 0;
+
+ markSeen(jsAst.BaseVisitor visitor) {
+ _rc++;
+ if (_rc == 1) entry.accept(visitor);
+ }
+
+ int compareTo(_MetadataEntry other) => other._rc - this._rc;
+}
+
+abstract class Placeholder implements jsAst.DeferredNumber {
+ bind(_MetadataEntry entry);
+}
+
+class _ForwardingMetadataEntry extends _MetadataEntry implements Placeholder {
+ _MetadataEntry _forwardTo;
+ var debug;
+
+ bool get isBound => _forwardTo != null;
+
+ _ForwardingMetadataEntry([this.debug]);
+
+ _MetadataEntry get forwardTo {
+ assert(isBound);
+ return _forwardTo;
+ }
+
+ jsAst.Expression get entry {
+ assert(isBound);
+ return forwardTo.entry;
+ }
+
+ int get value {
+ assert(isBound);
+ return forwardTo.value;
+ }
+
+ int get _rc => forwardTo._rc;
+
+ markSeen(jsAst.BaseVisitor visitor) => forwardTo.markSeen(visitor);
+
+ int compareTo(other) => forwardTo.compareTo(other);
+
+ bind(_MetadataEntry entry) {
+ assert(!isBound);
+ _forwardTo = entry;
+ }
+}
+
+class _MetadataList extends jsAst.DeferredExpression {
+ jsAst.Expression _value;
+
+ void setExpression(jsAst.Expression value) {
+ // TODO(herhut): Enable the below assertion once incremental mode is gone.
+ // assert(_value == null);
+ assert(value.precedenceLevel == this.precedenceLevel);
+ _value = value;
+ }
+
+ jsAst.Expression get value {
+ assert(_value != null);
+ return _value;
+ }
+
+ int get precedenceLevel => js_precedence.PRIMARY;
+}
+
class MetadataCollector {
final Compiler _compiler;
final Emitter _emitter;
- /// A list of JS expressions that represent metadata, parameter names and
- /// type variable types.
- final List<jsAst.Expression> globalMetadata = <jsAst.Expression>[];
+ /// A token for a list of expressions that represent metadata, parameter names
+ /// and type variable types.
+ final _MetadataList _globalMetadata = new _MetadataList();
+ jsAst.Expression get globalMetadata => _globalMetadata;
/// A map used to canonicalize the entries of globalMetadata.
- final Map<String, int> _globalMetadataMap = <String, int>{};
+ Map<String, _BoundMetadataEntry> _globalMetadataMap;
- /// A map with lists of JS expressions, one list for each output unit. The
- /// entries represent types including function types and typedefs.
- final Map<OutputUnit, List<jsAst.Expression>> types =
- <OutputUnit, List<jsAst.Expression>>{};
+ /// A map with a token for a lists of JS expressions, one token for each
+ /// output unit. Once finalized, the entries represent types including
+ /// function types and typedefs.
+ Map<OutputUnit, _MetadataList> _typesTokens =
+ new Map<OutputUnit, _MetadataList>();
+
+ jsAst.Expression getTypesForOutputUnit(OutputUnit outputUnit) {
+ return _typesTokens.putIfAbsent(outputUnit, () => new _MetadataList());
+ }
/// A map used to canonicalize the entries of types.
- final Map<OutputUnit, Map<String, int>> _typesMap =
- <OutputUnit, Map<String, int>>{};
+ Map<OutputUnit, Map<DartType, _BoundMetadataEntry>> _typesMap =
+ <OutputUnit, Map<DartType, _BoundMetadataEntry>>{};
- MetadataCollector(this._compiler, this._emitter);
+ // To support incremental compilation, we have to be able to eagerly emit
+ // metadata and add metadata later on. We use the below two counters for
+ // this.
+ int _globalMetadataCounter = 0;
+ int _globalTypesCounter = 0;
+
+ MetadataCollector(this._compiler, this._emitter) {
+ _globalMetadataMap = new Map<String, _BoundMetadataEntry>();
+ }
JavaScriptBackend get _backend => _compiler.backend;
TypeVariableHandler get _typeVariableHandler => _backend.typeVariableHandler;
@@ -64,51 +181,78 @@ class MetadataCollector {
});
}
- List<int> reifyDefaultArguments(FunctionElement function) {
+ List<jsAst.DeferredNumber> reifyDefaultArguments(FunctionElement function) {
FunctionSignature signature = function.functionSignature;
if (signature.optionalParameterCount == 0) return const [];
- List<int> defaultValues = <int>[];
+ List<jsAst.DeferredNumber> defaultValues = <jsAst.DeferredNumber>[];
for (ParameterElement element in signature.optionalParameters) {
ConstantValue constant =
_backend.constants.getConstantValueForVariable(element);
jsAst.Expression expression = (constant == null)
- ? null
+ ? new jsAst.LiteralNull()
: _emitter.constantReference(constant);
- defaultValues.add(addGlobalMetadata(expression));
+ defaultValues.add(_addGlobalMetadata(expression));
}
return defaultValues;
}
- int reifyMetadata(MetadataAnnotation annotation) {
+ jsAst.Expression reifyMetadata(MetadataAnnotation annotation) {
ConstantValue constant =
_backend.constants.getConstantValueForMetadata(annotation);
if (constant == null) {
_compiler.internalError(annotation, 'Annotation value is null.');
- return -1;
+ return null;
}
- return addGlobalMetadata(_emitter.constantReference(constant));
+ return _addGlobalMetadata(_emitter.constantReference(constant));
}
- int reifyType(DartType type, {bool ignoreTypeVariables: false}) {
+ jsAst.Expression reifyType(DartType type, {ignoreTypeVariables: false}) {
return reifyTypeForOutputUnit(type,
_compiler.deferredLoadTask.mainOutputUnit,
ignoreTypeVariables: ignoreTypeVariables);
}
- int reifyTypeForOutputUnit(DartType type, OutputUnit outputUnit,
- {bool ignoreTypeVariables: false}) {
- jsAst.Expression representation =
- _backend.rti.getTypeRepresentation(
- type,
- (variable) {
- if (ignoreTypeVariables) return new jsAst.LiteralNull();
- return js.number(
- _typeVariableHandler.reifyTypeVariable(
- variable.element));
- },
- (TypedefType typedef) {
- return _backend.isAccessibleByReflection(typedef.element);
- });
+ jsAst.Expression reifyTypeForOutputUnit(DartType type,
+ OutputUnit outputUnit,
+ {ignoreTypeVariables: false}) {
+ return addTypeInOutputUnit(type, outputUnit,
+ ignoreTypeVariables: ignoreTypeVariables);
+ }
+
+ jsAst.Expression reifyName(String name) {
+ return _addGlobalMetadata(js.string(name));
+ }
+
+ jsAst.Expression reifyExpression(jsAst.Expression expression) {
+ return _addGlobalMetadata(expression);
+ }
+
+ Placeholder getMetadataPlaceholder([debug]) {
+ return new _ForwardingMetadataEntry(debug);
+ }
+
+ _MetadataEntry _addGlobalMetadata(jsAst.Node node) {
+ String printed = jsAst.prettyPrint(node, _compiler).getText();
+ return _globalMetadataMap.putIfAbsent(printed, () {
+ _BoundMetadataEntry result = new _BoundMetadataEntry(node);
+ if (_compiler.hasIncrementalSupport) {
+ result.finalize(_globalMetadataCounter++);
+ }
+ return result;
+ });
+ }
+
+ jsAst.Expression _computeTypeRepresentation(DartType type,
+ {ignoreTypeVariables: false}) {
+ jsAst.Expression representation = _backend.rti.getTypeRepresentation(
+ type,
+ (variable) {
+ if (ignoreTypeVariables) return new jsAst.LiteralNull();
+ return _typeVariableHandler.reifyTypeVariable(variable.element);
+ },
+ (TypedefType typedef) {
+ return _backend.isAccessibleByReflection(typedef.element);
+ });
if (representation is jsAst.LiteralString) {
// We don't want the representation to be a string, since we use
@@ -117,42 +261,31 @@ class MetadataCollector {
NO_LOCATION_SPANNABLE, 'reified types should not be strings.');
}
- return addTypeInOutputUnit(representation, outputUnit);
- }
-
- int reifyName(String name) {
- return addGlobalMetadata(js('"$name"'));
- }
+ return representation;
- int addGlobalMetadata(jsAst.Expression expression) {
- // TODO(sigmund): consider adding an effient way to compare expressions
- String string = jsAst.prettyPrint(expression, _compiler).getText();
- return _globalMetadataMap.putIfAbsent(string, () {
- globalMetadata.add(expression);
- return globalMetadata.length - 1;
- });
}
- int addTypeInOutputUnit(jsAst.Expression type, OutputUnit outputUnit) {
- String string = jsAst.prettyPrint(type, _compiler).getText();
+ jsAst.Expression addTypeInOutputUnit(DartType type,
+ OutputUnit outputUnit,
+ {ignoreTypeVariables: false}) {
if (_typesMap[outputUnit] == null) {
- _typesMap[outputUnit] = <String, int>{};
+ _typesMap[outputUnit] = new Map<DartType, _BoundMetadataEntry>();
}
- return _typesMap[outputUnit].putIfAbsent(string, () {
-
- if (types[outputUnit] == null) {
- types[outputUnit] = <jsAst.Expression>[];
+ return _typesMap[outputUnit].putIfAbsent(type, () {
+ _BoundMetadataEntry result = new _BoundMetadataEntry(
+ _computeTypeRepresentation(type,
+ ignoreTypeVariables: ignoreTypeVariables));
+ if (_compiler.hasIncrementalSupport) {
+ result.finalize(_globalTypesCounter++);
}
-
- types[outputUnit].add(type);
- return types[outputUnit].length - 1;
+ return result;
});
}
- List<int> computeMetadata(FunctionElement element) {
+ List<jsAst.DeferredNumber> computeMetadata(FunctionElement element) {
return _compiler.withCurrentElement(element, () {
- if (!_mustEmitMetadataFor(element)) return const <int>[];
- List<int> metadata = <int>[];
+ if (!_mustEmitMetadataFor(element)) return const <jsAst.DeferredNumber>[];
+ List<jsAst.DeferredNumber> metadata = <jsAst.DeferredNumber>[];
Link link = element.metadata;
// TODO(ahe): Why is metadata sometimes null?
if (link != null) {
@@ -163,4 +296,94 @@ class MetadataCollector {
return metadata;
});
}
+
+ void countTokensInProgram(jsAst.Program program) {
+ TokenCounter visitor = new TokenCounter();
+ visitor.countTokens(program);
+ }
+
+ void finalizeTokens() {
+ bool checkTokensInTypes(OutputUnit outputUnit, entries) {
+ UnBoundDebugger debugger = new UnBoundDebugger(outputUnit);
+ for (_BoundMetadataEntry entry in entries) {
+ if (!entry.isUsed) continue;
+ if (debugger.findUnboundPlaceholders(entry.entry)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ void countTokensInTypes(Iterable<_BoundMetadataEntry> entries) {
+ TokenCounter counter = new TokenCounter();
+ entries.where((_BoundMetadataEntry e) => e._rc > 0)
+ .map((_BoundMetadataEntry e) => e.entry)
+ .forEach(counter.countTokens);
+ }
+
+ jsAst.ArrayInitializer finalizeMap(Map<dynamic, _BoundMetadataEntry> map) {
+ // When in incremental mode, we allocate entries eagerly.
+ if (_compiler.hasIncrementalSupport) {
+ return new jsAst.ArrayInitializer(map.values.toList());
+ }
+
+ bool isUsed(_BoundMetadataEntry entry) => entry.isUsed;
+ List<_BoundMetadataEntry> entries = map.values.where(isUsed).toList();
+ entries.sort();
+
+ // TODO(herhut): Bucket entries by index length and use a stable
+ // distribution within buckets.
+ int count = 0;
+ for (_BoundMetadataEntry entry in entries) {
+ entry.finalize(count++);
+ }
+
+ List<jsAst.Node> values =
+ entries.map((_BoundMetadataEntry e) => e.entry).toList();
+
+ return new jsAst.ArrayInitializer(values);
+ }
+
+ _globalMetadata.setExpression(finalizeMap(_globalMetadataMap));
+
+ _typesTokens.forEach((OutputUnit outputUnit, _MetadataList token) {
+ Map typesMap = _typesMap[outputUnit];
+ if (typesMap != null) {
+ assert(checkTokensInTypes(outputUnit, typesMap.values));
+ countTokensInTypes(typesMap.values);
+ token.setExpression(finalizeMap(typesMap));
+ } else {
+ token.setExpression(new jsAst.ArrayInitializer([]));
+ }
+ });
+ }
}
+
+class TokenCounter extends jsAst.BaseVisitor {
+ @override
+ visitDeferredNumber(jsAst.DeferredNumber token) {
+ if (token is _MetadataEntry) {
+ token.markSeen(this);
+ }
+ }
+
+ void countTokens(jsAst.Node node) => node.accept(this);
+}
+
+class UnBoundDebugger extends jsAst.BaseVisitor {
+ OutputUnit outputUnit;
+ bool _foundUnboundToken = false;
+
+ UnBoundDebugger(this.outputUnit);
+
+ @override
+ visitDeferredNumber(jsAst.DeferredNumber token) {
+ if (token is _ForwardingMetadataEntry && !token.isBound) {
+ _foundUnboundToken = true;
+ }
+ }
+
+ bool findUnboundPlaceholders(jsAst.Node node) {
+ node.accept(this);
+ return _foundUnboundToken;
+ }
+}
« no previous file with comments | « pkg/compiler/lib/src/js_emitter/js_emitter.dart ('k') | pkg/compiler/lib/src/js_emitter/model.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698