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

Unified Diff: lib/compiler/implementation/js_backend/backend.dart

Issue 10964016: Change the type inference for fields in dart2js (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Final round of changes Created 8 years, 3 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 | « lib/compiler/implementation/enqueue.dart ('k') | lib/compiler/implementation/ssa/codegen.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/compiler/implementation/js_backend/backend.dart
diff --git a/lib/compiler/implementation/js_backend/backend.dart b/lib/compiler/implementation/js_backend/backend.dart
index 144349642956c5eaad3266515c82aa4465b200ff..10245aab8042b730fd8774977b699ab90b952732 100644
--- a/lib/compiler/implementation/js_backend/backend.dart
+++ b/lib/compiler/implementation/js_backend/backend.dart
@@ -223,6 +223,214 @@ class HTypeList {
allUnknown ? "HTypeList.ALL_UNKNOWN" : "HTypeList $types";
}
+class FieldTypesRegistry {
+ final JavaScriptBackend backend;
+
+ /**
+ * For each class, [constructors] holds the set of constructors. If there is
+ * more than one constructor for a class it is currently not possible to
+ * infer the field types from construction, as the information collected does
+ * not correlate the generative constructors and generative constructor
+ * body/bodies.
+ */
+ final Map<Element, Set<ClassElement>> constructors;
+
+ /**
+ * The collected type information is stored in three maps. One for types
+ * assigned in the initializer list(s) [fieldInitializerTypeMap], one for
+ * types assigned in the constructor(s) [fieldConstructorTypeMap], and one
+ * for types assigned in the rest of the code, where the field can be
+ * resolved [fieldTypeMap].
+ *
+ * If a field has a type both from constructors and from the initializer
+ * list(s), then the type from the constructor(s) will owerride the one from
+ * the initializer list(s).
+ *
+ * Because the order in which generative constructors, generative constructor
+ * bodies and normal method/function bodies are compiled is undefined, and
+ * because they can all be recompiled, it is not possible to combine this
+ * information into one map at the moment.
+ */
+ final Map<Element, HType> fieldInitializerTypeMap;
+ final Map<Element, HType> fieldConstructorTypeMap;
+ final Map<Element, HType> fieldTypeMap;
+
+ /**
+ * The set of current names setter selectors used. If a named selector is
+ * used it is currently not possible to infer the type of the field.
+ */
+ final Set<SourceString> setterSelectorsUsed;
+
+ final Map<Element, Set<Element>> optimizedStaticFunctions;
+ final Map<Element, FunctionSet> optimizedFunctions;
+
+ FieldTypesRegistry(JavaScriptBackend backend)
+ : constructors = new Map<Element, Set<Element>>(),
+ fieldInitializerTypeMap = new Map<Element, HType>(),
+ fieldConstructorTypeMap = new Map<Element, HType>(),
+ fieldTypeMap = new Map<Element, HType>(),
+ setterSelectorsUsed = new Set<SourceString>(),
+ optimizedStaticFunctions = new Map<Element, Set<Element>>(),
+ optimizedFunctions = new Map<Element, FunctionSet>(),
+ this.backend = backend;
+
+ Compiler get compiler => backend.compiler;
+
+ void scheduleRecompilation(Element field) {
+ Set optimizedStatics = optimizedStaticFunctions[field];
+ if (optimizedStatics != null) {
+ optimizedStatics.forEach(backend.scheduleForRecompilation);
+ optimizedStaticFunctions.remove(field);
+ }
+ FunctionSet optimized = optimizedFunctions[field];
+ if (optimized != null) {
+ optimized.forEach(backend.scheduleForRecompilation);
+ optimizedFunctions.remove(field);
+ }
+ }
+
+ int constructorCount(Element element) {
+ assert(element.isClass());
+ Set<Element> ctors = constructors[element];
+ return ctors === null ? 0 : ctors.length;
+ }
+
+ void registerFieldType(Map<Element, HType> typeMap,
+ Element field,
+ HType type) {
+ assert(field.isField());
+ HType before = optimisticFieldType(field);
+
+ HType oldType = typeMap[field];
+ HType newType;
+
+ if (oldType != null) {
+ newType = oldType.union(type);
+ } else {
+ newType = type;
+ }
+ typeMap[field] = newType;
+ if (oldType != newType) {
+ scheduleRecompilation(field);
+ }
+ }
+
+ void registerConstructor(Element element) {
+ assert(element.isGenerativeConstructor());
+ Element cls = element.enclosingElement;
+ constructors.putIfAbsent(cls, () => new Set<Element>());
+ Set<Element> ctors = constructors[cls];
+ if (ctors.contains(element)) return;
+ ctors.add(element);
+ // We cannot infer field types for classes with more than one constructor.
+ // When the second constructor is seen, recompile all functions relying on
+ // optimistic field types for that class.
+ // TODO(sgjesse): Handle field types for classes with more than one
+ // constructor.
+ if (ctors.length == 2) {
+ optimizedFunctions.forEach((Element field, _) {
+ if (field.enclosingElement === cls) {
+ scheduleRecompilation(field);
+ }
+ });
+ }
+ }
+
+ void registerFieldInitializer(Element field, HType type) {
+ registerFieldType(fieldInitializerTypeMap, field, type);
+ }
+
+ void registerFieldConstructor(Element field, HType type) {
+ registerFieldType(fieldConstructorTypeMap, field, type);
+ }
+
+ void registerFieldSetter(FunctionElement element, Element field, HType type) {
+ HType initializerType = fieldInitializerTypeMap[field];
+ HType constructorType = fieldConstructorTypeMap[field];
+ HType setterType = fieldTypeMap[field];
+ if (type == HType.UNKNOWN
+ && initializerType == null
+ && constructorType == null
+ && setterType == null) {
+ // Don't register UNKONWN if there is currently no type information
+ // present for the field. Instead register the function holding the
+ // setter for recompilation if better type information for the field
+ // becomes available.
+ registerOptimizedFunction(element, field, type);
+ return;
+ }
+ registerFieldType(fieldTypeMap, field, type);
+ }
+
+ void addedDynamicSetter(Selector setter, HType type) {
+ // Field type optimizations are disabled for all fields matching a
+ // setter selector.
+ assert(setter.isSetter());
+ // TODO(sgjesse): Take the type of the setter into account.
+ if (setterSelectorsUsed.contains(setter.name)) return;
+ setterSelectorsUsed.add(setter.name);
+ optimizedStaticFunctions.forEach((Element field, _) {
+ if (field.name == setter.name) {
+ scheduleRecompilation(field);
+ }
+ });
+ optimizedFunctions.forEach((Element field, _) {
+ if (field.name == setter.name) {
+ scheduleRecompilation(field);
+ }
+ });
+ }
+
+ HType optimisticFieldType(Element field) {
+ assert(field.isField());
+ if (constructorCount(field.enclosingElement) > 1) {
+ return HType.UNKNOWN;
+ }
+ if (setterSelectorsUsed.contains(field.name)) {
+ return HType.UNKNOWN;
+ }
+ HType initializerType = fieldInitializerTypeMap[field];
+ HType constructorType = fieldConstructorTypeMap[field];
+ if (initializerType === null && constructorType === null) {
+ // If there are no constructor type information return UNKNOWN. This
+ // ensures that the function will be recompiled if useful constructor
+ // type information becomes available.
+ return HType.UNKNOWN;
+ }
+ // A type set through the constructor overrides the type from the
+ // initializer list.
+ HType result = constructorType != null ? constructorType : initializerType;
+ HType type = fieldTypeMap[field];
+ if (type !== null) result = result.union(type);
+ return result;
+ }
+
+ void registerOptimizedFunction(FunctionElement element,
+ Element field,
+ HType type) {
+ assert(field.isField());
+ if (Elements.isStaticOrTopLevel(element)) {
+ optimizedStaticFunctions.putIfAbsent(
+ field, () => new Set<Element>());
+ optimizedStaticFunctions[field].add(element);
+ } else {
+ optimizedFunctions.putIfAbsent(
+ field, () => new FunctionSet(backend.compiler));
+ optimizedFunctions[field].add(element);
+ }
+ }
+
+ void dump() {
+ Set<Element> allFields = new Set<Element>();
+ fieldInitializerTypeMap.getKeys().forEach(allFields.add);
+ fieldConstructorTypeMap.getKeys().forEach(allFields.add);
+ fieldTypeMap.getKeys().forEach(allFields.add);
+ allFields.forEach((Element field) {
+ print("Inferred $field has type ${optimisticFieldType(field)}");
+ });
+ }
+}
+
class ArgumentTypesRegistry {
final JavaScriptBackend backend;
@@ -385,10 +593,9 @@ class ArgumentTypesRegistry {
return found !== null ? found : HTypeList.ALL_UNKNOWN;
}
- void registerOptimization(Element element,
- HTypeList parameterTypes,
- OptionalParameterTypes defaultValueTypes) {
- assert(invariant(element, element.isDeclaration));
+ void registerOptimizedFunction(Element element,
+ HTypeList parameterTypes,
+ OptionalParameterTypes defaultValueTypes) {
if (Elements.isStaticOrTopLevelFunction(element)) {
if (parameterTypes.allUnknown) {
optimizedStaticFunctions.remove(element);
@@ -443,10 +650,6 @@ class JavaScriptBackend extends Backend {
*/
ClassElement jsIndexingBehaviorInterface;
- final Map<Element, Map<Element, HType>> fieldInitializers;
- final Map<Element, Map<Element, HType>> fieldConstructorSetters;
- final Map<Element, Map<Element, HType>> fieldSettersType;
-
final Map<Element, ReturnInfo> returnInfo;
/**
@@ -456,16 +659,14 @@ class JavaScriptBackend extends Backend {
*/
final List<Element> invalidateAfterCodegen;
ArgumentTypesRegistry argumentTypes;
+ FieldTypesRegistry fieldTypes;
List<CompilerTask> get tasks {
return <CompilerTask>[builder, optimizer, generator, emitter];
}
JavaScriptBackend(Compiler compiler, bool generateSourceMap)
- : fieldInitializers = new Map<Element, Map<Element, HType>>(),
- fieldConstructorSetters = new Map<Element, Map<Element, HType>>(),
- fieldSettersType = new Map<Element, Map<Element, HType>>(),
- namer = new Namer(compiler),
+ : namer = new Namer(compiler),
returnInfo = new Map<Element, ReturnInfo>(),
invalidateAfterCodegen = new List<Element>(),
super(compiler, constantSystem: JAVA_SCRIPT_CONSTANT_SYSTEM) {
@@ -474,6 +675,7 @@ class JavaScriptBackend extends Backend {
optimizer = new SsaOptimizerTask(this);
generator = new SsaCodeGeneratorTask(this);
argumentTypes = new ArgumentTypesRegistry(this);
+ fieldTypes = new FieldTypesRegistry(this);
}
Element get cyclicThrowHelper {
@@ -516,13 +718,13 @@ class JavaScriptBackend extends Backend {
}
HGraph graph = builder.build(work);
- optimizer.optimize(work, graph);
+ optimizer.optimize(work, graph, false);
if (work.allowSpeculativeOptimization
&& optimizer.trySpeculativeOptimizations(work, graph)) {
CodeBuffer codeBuffer = generator.generateBailoutMethod(work, graph);
compiler.codegenWorld.addBailoutCode(work, codeBuffer);
optimizer.prepareForSpeculativeOptimizations(work, graph);
- optimizer.optimize(work, graph);
+ optimizer.optimize(work, graph, true);
}
CodeBuffer codeBuffer = generator.generateCode(work, graph);
compiler.codegenWorld.addGeneratedCode(work, codeBuffer);
@@ -539,106 +741,6 @@ class JavaScriptBackend extends Backend {
emitter.assembleProgram();
}
- void updateFieldInitializers(Element field, HType propagatedType) {
- assert(field.isField());
- assert(field.isMember());
- Map<Element, HType> fields =
- fieldInitializers.putIfAbsent(
- field.getEnclosingClass(), () => new Map<Element, HType>());
- if (!fields.containsKey(field)) {
- fields[field] = propagatedType;
- } else {
- fields[field] = fields[field].union(propagatedType);
- }
- }
-
- HType typeFromInitializersSoFar(Element field) {
- assert(field.isField());
- assert(field.isMember());
- if (!fieldInitializers.containsKey(field.getEnclosingClass())) {
- return HType.CONFLICTING;
- }
- Map<Element, HType> fields = fieldInitializers[field.getEnclosingClass()];
- return fields[field];
- }
-
- void updateFieldConstructorSetters(Element field, HType type) {
- assert(field.isField());
- assert(field.isMember());
- Map<Element, HType> fields =
- fieldConstructorSetters.putIfAbsent(
- field.getEnclosingClass(), () => new Map<Element, HType>());
- if (!fields.containsKey(field)) {
- fields[field] = type;
- } else {
- fields[field] = fields[field].union(type);
- }
- }
-
- // Check if this field is set in the constructor body.
- bool hasConstructorBodyFieldSetter(Element field) {
- ClassElement enclosingClass = field.getEnclosingClass();
- if (!fieldConstructorSetters.containsKey(enclosingClass)) {
- return false;
- }
- return fieldConstructorSetters[enclosingClass][field] != null;
- }
-
- // Provide an optimistic estimate of the type of a field after construction.
- // If the constructor body has setters for fields returns HType.UNKNOWN.
- // This only takes the initializer lists and field assignments in the
- // constructor body into account. The constructor body might have method calls
- // that could alter the field.
- HType optimisticFieldTypeAfterConstruction(Element field) {
- assert(field.isField());
- assert(field.isMember());
-
- ClassElement classElement = field.getEnclosingClass();
- if (hasConstructorBodyFieldSetter(field)) {
- // If there are field setters but there is only constructor then the type
- // of the field is determined by the assignments in the constructor
- // body.
- var constructors = classElement.constructors;
- if (constructors.head !== null && constructors.tail.isEmpty()) {
- return fieldConstructorSetters[classElement][field];
- } else {
- return HType.UNKNOWN;
- }
- } else if (fieldInitializers.containsKey(classElement)) {
- HType type = fieldInitializers[classElement][field];
- return type == null ? HType.CONFLICTING : type;
- } else {
- return HType.CONFLICTING;
- }
- }
-
- void updateFieldSetters(Element field, HType type) {
- assert(field.isField());
- assert(field.isMember());
- Map<Element, HType> fields =
- fieldSettersType.putIfAbsent(
- field.getEnclosingClass(), () => new Map<Element, HType>());
- if (!fields.containsKey(field)) {
- fields[field] = type;
- } else {
- fields[field] = fields[field].union(type);
- }
- }
-
- // Returns the type that field setters are setting the field to based on what
- // have been seen during compilation so far.
- HType fieldSettersTypeSoFar(Element field) {
- assert(field.isField());
- assert(field.isMember());
- ClassElement enclosingClass = field.getEnclosingClass();
- if (!fieldSettersType.containsKey(enclosingClass)) {
- return HType.CONFLICTING;
- }
- Map<Element, HType> fields = fieldSettersType[enclosingClass];
- if (!fields.containsKey(field)) return HType.CONFLICTING;
- return fields[field];
- }
-
/**
* Documentation wanted -- johnniwinther
*
@@ -707,10 +809,16 @@ class JavaScriptBackend extends Backend {
OptionalParameterTypes defaultValueTypes) {
assert(invariant(element, element.isDeclaration));
if (element.parameterCount(compiler) == 0) return;
- argumentTypes.registerOptimization(
+ argumentTypes.registerOptimizedFunction(
element, parameterTypes, defaultValueTypes);
}
+ registerFieldTypesOptimization(FunctionElement element,
+ Element field,
+ HType type) {
+ fieldTypes.registerOptimizedFunction(element, field, type);
+ }
+
/**
* Documentation wanted -- johnniwinther
*
@@ -755,6 +863,30 @@ class JavaScriptBackend extends Backend {
});
}
+ void registerConstructor(Element element) {
+ fieldTypes.registerConstructor(element);
+ }
+
+ void registerFieldInitializer(Element field, HType type) {
+ fieldTypes.registerFieldInitializer(field, type);
+ }
+
+ void registerFieldConstructor(Element field, HType type) {
+ fieldTypes.registerFieldConstructor(field, type);
+ }
+
+ void registerFieldSetter(FunctionElement element, Element field, HType type) {
+ fieldTypes.registerFieldSetter(element, field, type);
+ }
+
+ void addedDynamicSetter(Selector setter, HType type) {
+ fieldTypes.addedDynamicSetter(setter, type);
+ }
+
+ HType optimisticFieldType(Element element) {
+ return fieldTypes.optimisticFieldType(element);
+ }
+
SourceString getCheckedModeHelper(DartType type) {
Element element = type.element;
bool nativeCheck =
« no previous file with comments | « lib/compiler/implementation/enqueue.dart ('k') | lib/compiler/implementation/ssa/codegen.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698