| 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 =
|
|
|