| Index: lib/compiler/implementation/ssa/optimize.dart
|
| diff --git a/lib/compiler/implementation/ssa/optimize.dart b/lib/compiler/implementation/ssa/optimize.dart
|
| index d0f9286c7f4fde6c3abc6399835788b985f2926c..c8ef7e5129f39271f2572ead4d3d76bbff06f9c1 100644
|
| --- a/lib/compiler/implementation/ssa/optimize.dart
|
| +++ b/lib/compiler/implementation/ssa/optimize.dart
|
| @@ -37,7 +37,7 @@ class SsaOptimizerTask extends CompilerTask {
|
| new SsaGlobalValueNumberer(compiler),
|
| new SsaCodeMotion(),
|
| new SsaDeadCodeEliminator(),
|
| - new SsaProcessRecompileCandidates(backend, work)];
|
| + new SsaRegisterRecompilationCandidates(backend, work)];
|
| runPhases(graph, phases);
|
| });
|
| }
|
| @@ -46,6 +46,7 @@ class SsaOptimizerTask extends CompilerTask {
|
| return measure(() {
|
| // Run the phases that will generate type guards.
|
| List<OptimizationPhase> phases = <OptimizationPhase>[
|
| + new SsaRecompilationFieldTypePropagator(backend, work),
|
| new SsaSpeculativeTypePropagator(compiler),
|
| new SsaTypeGuardInserter(compiler, work),
|
| new SsaEnvironmentBuilder(compiler),
|
| @@ -1156,124 +1157,23 @@ class SsaTypeConversionInserter extends HBaseVisitor
|
| }
|
| }
|
|
|
| -class SsaProcessRecompileCandidates
|
| - extends HBaseVisitor implements OptimizationPhase {
|
| - final String name = "SsaProcessRecompileCandidates";
|
| +
|
| +// Base class for the handling of recompilation based on inferred
|
| +// field types.
|
| +class BaseRecompilationVisitor extends HBaseVisitor {
|
| final JavaScriptBackend backend;
|
| final WorkItem work;
|
| - HGraph graph;
|
| Compiler get compiler() => backend.compiler;
|
|
|
| - SsaProcessRecompileCandidates(this.backend, this.work);
|
| -
|
| - void visitGraph(HGraph visitee) {
|
| - graph = visitee;
|
| - visitDominatorTree(visitee);
|
| - }
|
| + BaseRecompilationVisitor(this.backend, this.work);
|
|
|
| - void visitFieldGet(HFieldGet node) {
|
| - if (!node.element.enclosingElement.isClass()) return;
|
| - Element field = node.element;
|
| - HType type = backend.optimisticFieldTypeAfterConstruction(field);
|
| - if (!type.isUnknown()) {
|
| - switch (compiler.phase) {
|
| - case Compiler.PHASE_COMPILING:
|
| - // Recompile 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.
|
| - compiler.enqueuer.codegen.registerRecompilationCandidate(
|
| - work.element);
|
| - break;
|
| - case Compiler.PHASE_RECOMPILING:
|
| - if (!type.isConflicting()) {
|
| - // Check if optimistic type is based on a setter in the
|
| - // constructor body.
|
| - if (backend.hasConstructorBodyFieldSetter(field)) {
|
| - // If there are no other field setters then the one in
|
| - // the constructor body, the type is guaranteed for this
|
| - // field after construction.
|
| - assert(!node.element.isGenerativeConstructorBody());
|
| - if (!compiler.codegenWorld.hasInvokedSetter(field, compiler)) {
|
| - node.guaranteedType =
|
| - type.union(backend.fieldSettersTypeSoFar(node.element));
|
| - } else {
|
| - node.propagatedType =
|
| - type.union(backend.fieldSettersTypeSoFar(node.element));
|
| - }
|
| - } else {
|
| - // If there are no setters the initializer list type is
|
| - // guaranteed to remain constant.
|
| - //
|
| - // TODO(ager): Why is this treated differently from the
|
| - // case above? It seems to me that we could/should use
|
| - // the union of the types for the field setters and the
|
| - // initializer list here? It would give the same when
|
| - // there are none and potentially better information for
|
| - // more cases.
|
| - if (!compiler.codegenWorld.hasFieldSetter(field, compiler) &&
|
| - !compiler.codegenWorld.hasInvokedSetter(field, compiler)) {
|
| - node.guaranteedType = type;
|
| - } else {
|
| - node.propagatedType = type;
|
| - }
|
| - }
|
| - }
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - HInstruction visitEquals(HEquals 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 integer literal.
|
| - if (other != null &&
|
| - other.isConstantInteger() &&
|
| - field.element != null &&
|
| - field.element.enclosingElement.isClass()) {
|
| - // Calculate the field type from the information available.
|
| - HType type =
|
| - backend.fieldSettersTypeSoFar(field.element).union(
|
| - backend.typeFromInitializersSoFar(field.element));
|
| - if (!type.isUnknown()) {
|
| - switch (compiler.phase) {
|
| - case Compiler.PHASE_COMPILING:
|
| - compiler.enqueuer.codegen.registerRecompilationCandidate(
|
| - work.element);
|
| - break;
|
| - case 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 the calculated, but the fact that the class
|
| - // itself stick to this type in the field is still a strong
|
| - // signal to indicate the expected type of the field.
|
| - field.propagatedType = type;
|
| - graph.highTypeLikelyhood = true;
|
| - } else {
|
| - // If there are no invoked setters we know the type of this
|
| - // field for sure.
|
| - field.guaranteedType = type;
|
| - }
|
| - break;
|
| - default:
|
| - assert(false);
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - }
|
| + abstract void handleFieldGet(HFieldGet node, HType type);
|
| + abstract void handleFieldNumberOperation(HFieldGet field, HType type);
|
|
|
| - HInstruction visitBinaryArithmetic(HBinaryArithmetic node) {
|
| + // 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;
|
| @@ -1284,46 +1184,147 @@ class SsaProcessRecompileCandidates
|
| field = node.right;
|
| other = node.left;
|
| }
|
| - // Check that the other operand is a number and that we have type
|
| - // information for the field get.
|
| + // 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.enclosingElement.isClass()) {
|
| - // If we have type information for the field and it contains
|
| - // NUMBER, we mark for recompilation.
|
| + // 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) {
|
| - switch (compiler.phase) {
|
| - case Compiler.PHASE_COMPILING:
|
| - compiler.enqueuer.codegen.registerRecompilationCandidate(
|
| - work.element);
|
| - break;
|
| - case Compiler.PHASE_RECOMPILING:
|
| - if (compiler.codegenWorld.hasInvokedSetter(fieldElement,
|
| - 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.
|
| - field.propagatedType = type;
|
| - graph.highTypeLikelyhood = true;
|
| - } else {
|
| - // If there are no invoked setters we know the type of
|
| - // this field for sure.
|
| - field.guaranteedType = type;
|
| - }
|
| - break;
|
| - default:
|
| - assert(false);
|
| - break;
|
| + handleFieldNumberOperation(field, fieldType);
|
| + }
|
| + }
|
| + }
|
| +
|
| + void visitFieldGet(HFieldGet node) {
|
| + if (!node.element.isInstanceMember()) return;
|
| + Element field = node.element;
|
| + 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);
|
| + }
|
| + }
|
| +
|
| + HInstruction visitEquals(HEquals node) {
|
| + checkFieldNumberOperation(node);
|
| + }
|
| +
|
| + HInstruction visitBinaryArithmetic(HBinaryArithmetic node) {
|
| + checkFieldNumberOperation(node);
|
| + }
|
| +}
|
| +
|
| +
|
| +// Visitor that registers candidates for recompilation.
|
| +class SsaRegisterRecompilationCandidates
|
| + extends BaseRecompilationVisitor implements OptimizationPhase {
|
| + final String name = "SsaRegisterRecompileCandidates";
|
| + HGraph graph;
|
| +
|
| + SsaRegisterRecompilationCandidates(
|
| + JavaScriptBackend backend, WorkItem work) : super(backend, work);
|
| +
|
| + void visitGraph(HGraph visitee) {
|
| + graph = visitee;
|
| + if (compiler.phase == Compiler.PHASE_COMPILING) {
|
| + visitDominatorTree(visitee);
|
| + }
|
| + }
|
| +
|
| + void handleFieldGet(HFieldGet node, HType type) {
|
| + assert(compiler.phase == Compiler.PHASE_COMPILING);
|
| + compiler.enqueuer.codegen.registerRecompilationCandidate(
|
| + work.element);
|
| + }
|
| +
|
| + void handleFieldNumberOperation(HFieldGet node, HType type) {
|
| + assert(compiler.phase == Compiler.PHASE_COMPILING);
|
| + compiler.enqueuer.codegen.registerRecompilationCandidate(
|
| + work.element);
|
| + }
|
| +}
|
| +
|
| +
|
| +// 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) : super(backend, work);
|
| +
|
| + 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()) {
|
| + Element element = field.element;
|
| + // Check if optimistic type is based on a setter in the
|
| + // constructor body.
|
| + if (backend.hasConstructorBodyFieldSetter(element)) {
|
| + // If there are no other field setters then the one in
|
| + // the constructor body, the type is guaranteed for this
|
| + // field after construction.
|
| + assert(!element.isGenerativeConstructorBody());
|
| + if (!compiler.codegenWorld.hasInvokedSetter(element, compiler)) {
|
| + field.guaranteedType =
|
| + type.union(backend.fieldSettersTypeSoFar(element));
|
| + } else {
|
| + field.propagatedType =
|
| + type.union(backend.fieldSettersTypeSoFar(element));
|
| + }
|
| + } else {
|
| + // If there are no setters the initializer list type is
|
| + // guaranteed to remain constant.
|
| + //
|
| + // TODO(ager): Why is this treated differently from the
|
| + // case above? It seems to me that we could/should use
|
| + // the union of the types for the field setters and the
|
| + // initializer list here? It would give the same when
|
| + // there are none and potentially better information for
|
| + // more cases.
|
| + if (!compiler.codegenWorld.hasFieldSetter(element, compiler) &&
|
| + !compiler.codegenWorld.hasInvokedSetter(element, compiler)) {
|
| + field.guaranteedType = type;
|
| + } else {
|
| + field.propagatedType = type;
|
| }
|
| }
|
| }
|
| }
|
| +
|
| + 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.
|
| + field.propagatedType = type;
|
| + } else {
|
| + // If there are no invoked setters we know the type of
|
| + // this field for sure.
|
| + field.guaranteedType = type;
|
| + }
|
| + }
|
| }
|
|
|