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

Unified Diff: lib/compiler/implementation/ssa/optimize.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
Index: lib/compiler/implementation/ssa/optimize.dart
diff --git a/lib/compiler/implementation/ssa/optimize.dart b/lib/compiler/implementation/ssa/optimize.dart
index b019498dbf3ad920d500bb87f646ccb58298080c..568355aefd170c4d6c4218b21cee18ef629cc081 100644
--- a/lib/compiler/implementation/ssa/optimize.dart
+++ b/lib/compiler/implementation/ssa/optimize.dart
@@ -26,7 +26,7 @@ class SsaOptimizerTask extends CompilerTask {
compiler.tracer.traceGraph(phase.name, graph);
}
- void optimize(WorkItem work, HGraph graph) {
+ void optimize(WorkItem work, HGraph graph, bool speculative) {
ConstantSystem constantSystem = compiler.backend.constantSystem;
JavaScriptItemCompilationContext context = work.compilationContext;
HTypeMap types = context.types;
@@ -46,9 +46,11 @@ class SsaOptimizerTask extends CompilerTask {
// Previous optimizations may have generated new
// opportunities for constant folding.
new SsaConstantFolder(constantSystem, backend, work, types),
- new SsaDeadCodeEliminator(types),
- new SsaRegisterRecompilationCandidates(backend, work, types)];
+ new SsaDeadCodeEliminator(types)];
runPhases(graph, phases);
+ if (!speculative) {
+ runPhase(graph, new SsaConstructionFieldTypes(backend, work, types));
+ }
});
}
@@ -62,7 +64,6 @@ class SsaOptimizerTask extends CompilerTask {
return measure(() {
// Run the phases that will generate type guards.
List<OptimizationPhase> phases = <OptimizationPhase>[
- new SsaRecompilationFieldTypePropagator(backend, work, types),
new SsaSpeculativeTypePropagator(compiler, types),
new SsaTypeGuardInserter(compiler, work, types),
new SsaEnvironmentBuilder(compiler),
@@ -612,23 +613,15 @@ class SsaConstantFolder extends HBaseVisitor implements OptimizationPhase {
// initializer list.
isFinalOrConst = true;
}
- if (!isFinalOrConst &&
- !compiler.codegenWorld.hasInvokedSetter(field, compiler) &&
- !compiler.codegenWorld.hasFieldSetter(field, compiler)) {
- switch (compiler.phase) {
- case Compiler.PHASE_COMPILING:
- compiler.enqueuer.codegen.registerRecompilationCandidate(
- work.element);
- break;
- case Compiler.PHASE_RECOMPILING:
- // If field is not final or const but no setters are used then the
- // field might be considered final anyway as it will be either
- // un-initialized or initialized in the constructor initializer list.
- isFinalOrConst = true;
- break;
- }
+ HFieldGet result = new HFieldGet(
+ field, node.inputs[0], isAssignable: !isFinalOrConst);
+ HType type = backend.optimisticFieldType(field);
+ if (type != null) {
+ result.guaranteedType = type;
+ backend.registerFieldTypesOptimization(
+ work.element, field, result.guaranteedType);
}
- return new HFieldGet(field, node.inputs[0], isAssignable: !isFinalOrConst);
+ return result;
}
HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) {
@@ -1220,159 +1213,146 @@ class SsaTypeConversionInserter extends HBaseVisitor
}
-// Base class for the handling of recompilation based on inferred
-// field types.
-class BaseRecompilationVisitor extends HBaseVisitor {
+// Analyze the constructors to see if some fields will always have a specific
+// type after construction. If this is the case we can ignore the type given
+// by the field initializer. This is especially useful when the field
+// initializer is initializing the field to null.
+class SsaConstructionFieldTypes
+ extends HBaseVisitor implements OptimizationPhase {
final JavaScriptBackend backend;
final WorkItem work;
final HTypeMap types;
- Compiler get compiler => backend.compiler;
+ final String name = "SsaConstructionFieldTypes";
+ final Set<HInstruction> thisUsers;
+ final Set<Element> allSetters;
+ final Map<HBasicBlock, Map<Element, HType>> blockFieldSetters;
+ bool thisExposed = false;
+ HGraph currentGraph;
+ HInstruction thisInstruction;
+ Map<Element, HType> currentFieldSetters;
+
+ SsaConstructionFieldTypes(JavaScriptBackend this.backend,
+ WorkItem this.work,
+ HTypeMap this.types)
+ : thisUsers = new Set<HInstruction>(),
+ allSetters = new Set<Element>(),
+ blockFieldSetters = new Map<HBasicBlock, Map<Element, HType>>();
- BaseRecompilationVisitor(this.backend, this.work, this.types);
-
- abstract void handleFieldGet(HFieldGet node, HType type);
- abstract void handleFieldNumberOperation(HFieldGet field, HType type);
-
- // Checks if the binary invocation operates on a field and a
- // constant number. If it does [handleFieldNumberOperation] is
- // called with the field and the type inferred for the field so far.
- void checkFieldNumberOperation(HInvokeBinary node) {
- // Determine if one of the operands is an HFieldGet.
- HFieldGet field;
- HInstruction other;
- if (node.left is HFieldGet) {
- field = node.left;
- other = node.right;
- } else if (node.right is HFieldGet) {
- field = node.right;
- other = node.left;
- }
- // Try to optimize the case where a field which is known to always
- // be an integer is compared with a constant number.
- if (other != null &&
- other.isConstantNumber() &&
- field.element != null &&
- field.element.isMember()) {
- // Calculate the field type from the information available. If
- // we have type information for the field and it contains NUMBER
- // we use it as a candidate for recompilation.
- Element fieldElement = field.element;
- HType fieldSettersType = backend.fieldSettersTypeSoFar(fieldElement);
- HType initializersType = backend.typeFromInitializersSoFar(fieldElement);
- HType fieldType = fieldSettersType.union(initializersType);
- HType type = HType.NUMBER.union(fieldType);
- if (type == HType.NUMBER) {
- handleFieldNumberOperation(field, fieldType);
- }
+ void visitGraph(HGraph graph) {
+ currentGraph = graph;
+ if (!work.element.isGenerativeConstructorBody() &&
+ !work.element.isGenerativeConstructor()) return;
+ visitDominatorTree(graph);
+ if (work.element.isGenerativeConstructor()) {
+ backend.registerConstructor(work.element);
}
}
- void visitFieldGet(HFieldGet node) {
- if (!node.element.isInstanceMember()) return;
- Element field = node.element;
- if (field != null) {
- HType type = backend.optimisticFieldTypeAfterConstruction(field);
- if (!type.isUnknown()) {
- // Allow handling even if we haven't seen any types for this
- // field yet. There might still be only one setter in an
- // initializer list or constructor body and recompilation
- // can therefore pay off.
- handleFieldGet(node, type);
+ visitBasicBlock(HBasicBlock block) {
+ if (block.predecessors.length == 0) {
+ // Create a new empty map for the first block.
+ currentFieldSetters = new Map<Element, HType>();
+ } else {
+ // Build a map which intersects the fields from all predecessors. For
+ // each field in this intersection it unions the types.
+ currentFieldSetters =
+ new Map.from(blockFieldSetters[block.predecessors[0]]);
+ // Loop headers are the only nodes with back edges.
+ if (!block.isLoopHeader()) {
+ for (int i = 1; i < block.predecessors.length; i++) {
+ Map<Element, HType> predecessorsFieldSetters =
+ blockFieldSetters[block.predecessors[i]];
+ Map<Element, HType> newFieldSetters = new Map<Element, HType>();
+ predecessorsFieldSetters.forEach((Element element, HType type) {
+ HType currentType = currentFieldSetters[element];
+ if (currentType != null) {
+ newFieldSetters[element] = currentType.union(type);
+ }
+ });
+ currentFieldSetters = newFieldSetters;
+ }
+ } else {
+ assert(block.predecessors.length <= 2);
}
}
+ block.forEachPhi((HPhi phi) => phi.accept(this));
+ block.forEachInstruction(
+ (HInstruction instruction) => instruction.accept(this));
+ assert(currentFieldSetters != null);
+ blockFieldSetters[block] = currentFieldSetters;
}
- HInstruction visitEquals(HEquals node) {
- checkFieldNumberOperation(node);
- }
-
- HInstruction visitBinaryArithmetic(HBinaryArithmetic node) {
- checkFieldNumberOperation(node);
+ visitInstruction(HInstruction instruction) {
+ // All instructions not explicitly handled below will flag the this
+ // exposure if using this.
+ thisExposed = thisExposed || thisUsers.contains(instruction);
}
-}
-
-
-// Visitor that registers candidates for recompilation.
-class SsaRegisterRecompilationCandidates
- extends BaseRecompilationVisitor implements OptimizationPhase {
- final String name = "SsaRegisterRecompileCandidates";
- HGraph graph;
-
- SsaRegisterRecompilationCandidates(JavaScriptBackend backend,
- WorkItem work,
- HTypeMap types)
- : super(backend, work, types);
- void visitGraph(HGraph visitee) {
- graph = visitee;
- if (compiler.phase == Compiler.PHASE_COMPILING) {
- visitDominatorTree(visitee);
+ visitPhi(HPhi phi) {
+ if (phi.inputs.indexOf(thisInstruction) != -1) {
+ thisUsers.addAll(phi.usedBy);
}
}
- void handleFieldGet(HFieldGet node, HType type) {
- assert(compiler.phase == Compiler.PHASE_COMPILING);
- compiler.enqueuer.codegen.registerRecompilationCandidate(
- work.element);
+ visitThis(HThis instruction) {
+ thisInstruction = instruction;
+ // Collect all users of this in a set to make the this exposed check simple
+ // and cheap.
+ thisUsers.addAll(instruction.usedBy);
}
- void handleFieldNumberOperation(HFieldGet node, HType type) {
- assert(compiler.phase == Compiler.PHASE_COMPILING);
- compiler.enqueuer.codegen.registerRecompilationCandidate(
- work.element);
+ visitFieldGet(HInstruction _) {
+ // The field get instruction is allowed to use this.
}
-}
-
-// Visitor that sets the known or suspected type of fields during
-// recompilation.
-class SsaRecompilationFieldTypePropagator
- extends BaseRecompilationVisitor implements OptimizationPhase {
- final String name = "SsaRecompilationFieldTypePropagator";
- HGraph graph;
-
- SsaRecompilationFieldTypePropagator(JavaScriptBackend backend,
- WorkItem work,
- HTypeMap types)
- : super(backend, work, types);
-
- void visitGraph(HGraph visitee) {
- graph = visitee;
- if (compiler.phase == Compiler.PHASE_RECOMPILING) {
- visitDominatorTree(visitee);
- }
- }
-
- void handleFieldGet(HFieldGet field, HType type) {
- assert(compiler.phase == Compiler.PHASE_RECOMPILING);
- if (!type.isConflicting()) {
- // If there are no invoked setters with this name, the union of
- // the types of the initializers and the setters is guaranteed
- // otherwise it is only speculative.
- Element element = field.element;
- assert(!element.isGenerativeConstructorBody());
- if (!compiler.codegenWorld.hasInvokedSetter(element, compiler)) {
- field.guaranteedType =
- type.union(backend.fieldSettersTypeSoFar(element));
- } else {
- types[field] = type.union(backend.fieldSettersTypeSoFar(element));
- }
- }
+ visitForeignNew(HForeignNew node) {
+ // The HForeignNew instruction is used in the generative constructor to
+ // initialize all fields in newly created objects. The fields are
+ // initialized to the value present in the initializer list or set to null
+ // if not otherwise initialized.
+ // Here we handle members in superclasses as well, as the handling of
+ // the generative constructor bodies will ensure, that the initializer
+ // type will not be used if the field is in any of these.
+ int j = 0;
+ node.element.forEachInstanceField(
+ includeBackendMembers: false,
+ includeSuperMembers: true,
+ f: (ClassElement enclosingClass, Element element) {
+ backend.registerFieldInitializer(element, types[node.inputs[j]]);
+ j++;
+ });
}
- void handleFieldNumberOperation(HFieldGet field, HType type) {
- assert(compiler.phase == Compiler.PHASE_RECOMPILING);
- if (compiler.codegenWorld.hasInvokedSetter(field.element, compiler)) {
- // If there are invoked setters we don't know for sure
- // that the field will hold a value of the calculated
- // type, but the fact that the class itself sticks to
- // this type for the field is still a strong signal
- // indicating the expected type of the field.
- types[field] = type;
- } else {
- // If there are no invoked setters we know the type of
- // this field for sure.
- field.guaranteedType = type;
+ visitFieldSet(HFieldSet node) {
+ Element field = node.element;
+ HInstruction value = node.value;
+ HType type = types[value];
+ allSetters.add(field);
+ // Don't handle fields defined in superclasses. Given that the field is
+ // always added to the [allSetters] set, setting a field defined in a
+ // superclass will get an inferred type of UNKNOWN.
+ if (work.element.getEnclosingClass() === field.getEnclosingClass() &&
+ value.hasGuaranteedType()) {
+ currentFieldSetters[field] = type;
+ }
+ }
+
+ visitExit(HExit node) {
+ // If this has been exposed then we cannot say anything about types after
+ // construction.
+ if (!thisExposed) {
+ // Register the known field types.
+ currentFieldSetters.forEach((Element element, HType type) {
+ backend.registerFieldConstructor(element, type);
+ allSetters.remove(element);
+ });
}
+
+ // For other fields having setters in the generative constructor body, set
+ // the type to UNKNOWN to avoid relying on the type set in the initializer
+ // list.
+ allSetters.forEach((Element element) {
+ backend.registerFieldConstructor(element, HType.UNKNOWN);
+ });
}
}
« no previous file with comments | « lib/compiler/implementation/ssa/codegen.dart ('k') | lib/compiler/implementation/universe/function_set.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698