| Index: sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart
|
| diff --git a/sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart b/sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart
|
| deleted file mode 100644
|
| index eb21bb402cb70999d5514ab5e0ceea01109835e3..0000000000000000000000000000000000000000
|
| --- a/sdk/lib/_internal/compiler/implementation/ssa/types_propagation.dart
|
| +++ /dev/null
|
| @@ -1,363 +0,0 @@
|
| -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -part of ssa;
|
| -
|
| -class SsaTypePropagator extends HBaseVisitor implements OptimizationPhase {
|
| - final Map<int, HInstruction> workmap = new Map<int, HInstruction>();
|
| - final List<int> worklist = new List<int>();
|
| - final Map<HInstruction, Function> pendingOptimizations =
|
| - new Map<HInstruction, Function>();
|
| -
|
| - final Compiler compiler;
|
| - final ClassWorld classWorld;
|
| - JavaScriptBackend get backend => compiler.backend;
|
| - String get name => 'type propagator';
|
| -
|
| - SsaTypePropagator(Compiler compiler)
|
| - : this.compiler = compiler,
|
| - this.classWorld = compiler.world;
|
| -
|
| - TypeMask computeType(HInstruction instruction) {
|
| - return instruction.accept(this);
|
| - }
|
| -
|
| - // Re-compute and update the type of the instruction. Returns
|
| - // whether or not the type was changed.
|
| - bool updateType(HInstruction instruction) {
|
| - // Compute old and new types.
|
| - TypeMask oldType = instruction.instructionType;
|
| - TypeMask newType = computeType(instruction);
|
| - assert(newType != null);
|
| - // We unconditionally replace the propagated type with the new type. The
|
| - // computeType must make sure that we eventually reach a stable state.
|
| - instruction.instructionType = newType;
|
| - return oldType != newType;
|
| - }
|
| -
|
| - void visitGraph(HGraph graph) {
|
| - visitDominatorTree(graph);
|
| - processWorklist();
|
| - }
|
| -
|
| - visitBasicBlock(HBasicBlock block) {
|
| - if (block.isLoopHeader()) {
|
| - block.forEachPhi((HPhi phi) {
|
| - // Set the initial type for the phi. We're not using the type
|
| - // the phi thinks it has because new optimizations may imply
|
| - // changing it.
|
| - // In theory we would need to mark
|
| - // the type of all other incoming edges as "unitialized" and take this
|
| - // into account when doing the propagation inside the phis. Just
|
| - // setting the propagated type is however easier.
|
| - phi.instructionType = phi.inputs[0].instructionType;
|
| - addToWorkList(phi);
|
| - });
|
| - } else {
|
| - block.forEachPhi((HPhi phi) {
|
| - if (updateType(phi)) {
|
| - addDependentInstructionsToWorkList(phi);
|
| - }
|
| - });
|
| - }
|
| -
|
| - HInstruction instruction = block.first;
|
| - while (instruction != null) {
|
| - if (updateType(instruction)) {
|
| - addDependentInstructionsToWorkList(instruction);
|
| - }
|
| - instruction = instruction.next;
|
| - }
|
| - }
|
| -
|
| - void processWorklist() {
|
| - do {
|
| - while (!worklist.isEmpty) {
|
| - int id = worklist.removeLast();
|
| - HInstruction instruction = workmap[id];
|
| - assert(instruction != null);
|
| - workmap.remove(id);
|
| - if (updateType(instruction)) {
|
| - addDependentInstructionsToWorkList(instruction);
|
| - }
|
| - }
|
| - // While processing the optimizable arithmetic instructions, we
|
| - // may discover better type information for dominated users of
|
| - // replaced operands, so we may need to take another stab at
|
| - // emptying the worklist afterwards.
|
| - processPendingOptimizations();
|
| - } while (!worklist.isEmpty);
|
| - }
|
| -
|
| -
|
| - void addToWorkList(HInstruction instruction) {
|
| - final int id = instruction.id;
|
| -
|
| - if (!workmap.containsKey(id)) {
|
| - worklist.add(id);
|
| - workmap[id] = instruction;
|
| - }
|
| - }
|
| -
|
| - TypeMask visitBinaryArithmetic(HBinaryArithmetic instruction) {
|
| - HInstruction left = instruction.left;
|
| - HInstruction right = instruction.right;
|
| - if (left.isInteger(compiler) && right.isInteger(compiler)) {
|
| - return backend.intType;
|
| - }
|
| - if (left.isDouble(compiler)) return backend.doubleType;
|
| - return backend.numType;
|
| - }
|
| -
|
| - TypeMask checkPositiveInteger(HBinaryArithmetic instruction) {
|
| - HInstruction left = instruction.left;
|
| - HInstruction right = instruction.right;
|
| - if (left.isPositiveInteger(compiler) && right.isPositiveInteger(compiler)) {
|
| - return backend.positiveIntType;
|
| - }
|
| - return visitBinaryArithmetic(instruction);
|
| - }
|
| -
|
| - TypeMask visitMultiply(HMultiply instruction) {
|
| - return checkPositiveInteger(instruction);
|
| - }
|
| -
|
| - TypeMask visitAdd(HAdd instruction) {
|
| - return checkPositiveInteger(instruction);
|
| - }
|
| -
|
| - TypeMask visitNegate(HNegate instruction) {
|
| - HInstruction operand = instruction.operand;
|
| - // We have integer subclasses that represent ranges, so widen any int
|
| - // subclass to full integer.
|
| - if (operand.isInteger(compiler)) return backend.intType;
|
| - return instruction.operand.instructionType;
|
| - }
|
| -
|
| - TypeMask visitInstruction(HInstruction instruction) {
|
| - assert(instruction.instructionType != null);
|
| - return instruction.instructionType;
|
| - }
|
| -
|
| - TypeMask visitPhi(HPhi phi) {
|
| - TypeMask candidateType = backend.emptyType;
|
| - for (int i = 0, length = phi.inputs.length; i < length; i++) {
|
| - TypeMask inputType = phi.inputs[i].instructionType;
|
| - candidateType = candidateType.union(inputType, classWorld);
|
| - }
|
| - return candidateType;
|
| - }
|
| -
|
| - TypeMask visitTypeConversion(HTypeConversion instruction) {
|
| - HInstruction input = instruction.checkedInput;
|
| - TypeMask inputType = input.instructionType;
|
| - TypeMask checkedType = instruction.checkedType;
|
| - if (instruction.isArgumentTypeCheck || instruction.isReceiverTypeCheck) {
|
| - // We must make sure a type conversion for receiver or argument check
|
| - // does not try to do an int check, because an int check is not enough.
|
| - // We only do an int check if the input is integer or null.
|
| - if (checkedType.containsOnlyNum(classWorld)
|
| - && !checkedType.containsOnlyDouble(classWorld)
|
| - && input.isIntegerOrNull(compiler)) {
|
| - instruction.checkedType = backend.intType;
|
| - } else if (checkedType.containsOnlyInt(classWorld)
|
| - && !input.isIntegerOrNull(compiler)) {
|
| - instruction.checkedType = backend.numType;
|
| - }
|
| - }
|
| -
|
| - TypeMask outputType = checkedType.intersection(inputType, classWorld);
|
| - if (outputType.isEmpty && !outputType.isNullable) {
|
| - // Intersection of double and integer conflicts (is empty), but JS numbers
|
| - // can be both int and double at the same time. For example, the input
|
| - // can be a literal double '8.0' that is marked as an integer (because 'is
|
| - // int' will return 'true'). What we really need to do is make the
|
| - // overlap between int and double values explicit in the TypeMask system.
|
| - if (inputType.containsOnlyInt(classWorld)
|
| - && checkedType.containsOnlyDouble(classWorld)) {
|
| - if (inputType.isNullable && checkedType.isNullable) {
|
| - outputType = backend.doubleType.nullable();
|
| - } else {
|
| - outputType = backend.doubleType;
|
| - }
|
| - }
|
| - }
|
| - return outputType;
|
| - }
|
| -
|
| - TypeMask visitTypeKnown(HTypeKnown instruction) {
|
| - HInstruction input = instruction.checkedInput;
|
| - return instruction.knownType.intersection(
|
| - input.instructionType, classWorld);
|
| - }
|
| -
|
| - void convertInput(HInvokeDynamic instruction,
|
| - HInstruction input,
|
| - TypeMask type,
|
| - int kind) {
|
| - Selector selector = (kind == HTypeConversion.RECEIVER_TYPE_CHECK)
|
| - ? instruction.selector
|
| - : null;
|
| - HTypeConversion converted = new HTypeConversion(
|
| - null, kind, type, input, selector);
|
| - instruction.block.addBefore(instruction, converted);
|
| - input.replaceAllUsersDominatedBy(instruction, converted);
|
| - }
|
| -
|
| - bool isCheckEnoughForNsmOrAe(HInstruction instruction,
|
| - TypeMask type) {
|
| - // In some cases, we want the receiver to be an integer,
|
| - // but that does not mean we will get a NoSuchMethodError
|
| - // if it's not: the receiver could be a double.
|
| - if (type.containsOnlyInt(classWorld)) {
|
| - // If the instruction's type is integer or null, the codegen
|
| - // will emit a null check, which is enough to know if it will
|
| - // hit a noSuchMethod.
|
| - return instruction.isIntegerOrNull(compiler);
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - // Add a receiver type check when the call can only hit
|
| - // [noSuchMethod] if the receiver is not of a specific type.
|
| - // Return true if the receiver type check was added.
|
| - bool checkReceiver(HInvokeDynamic instruction) {
|
| - assert(instruction.isInterceptedCall);
|
| - HInstruction receiver = instruction.inputs[1];
|
| - if (receiver.isNumber(compiler)) return false;
|
| - if (receiver.isNumberOrNull(compiler)) {
|
| - convertInput(instruction,
|
| - receiver,
|
| - receiver.instructionType.nonNullable(),
|
| - HTypeConversion.RECEIVER_TYPE_CHECK);
|
| - return true;
|
| - } else if (instruction.element == null) {
|
| - Iterable<Element> targets =
|
| - compiler.world.allFunctions.filter(instruction.selector);
|
| - if (targets.length == 1) {
|
| - Element target = targets.first;
|
| - ClassElement cls = target.enclosingClass;
|
| - TypeMask type = new TypeMask.nonNullSubclass(
|
| - cls.declaration, classWorld);
|
| - // TODO(ngeoffray): We currently only optimize on primitive
|
| - // types.
|
| - if (!type.satisfies(backend.jsIndexableClass, classWorld) &&
|
| - !type.containsOnlyNum(classWorld) &&
|
| - !type.containsOnlyBool(classWorld)) {
|
| - return false;
|
| - }
|
| - if (!isCheckEnoughForNsmOrAe(receiver, type)) return false;
|
| - instruction.element = target;
|
| - convertInput(instruction,
|
| - receiver,
|
| - type,
|
| - HTypeConversion.RECEIVER_TYPE_CHECK);
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - // Add an argument type check if the argument is not of a type
|
| - // expected by the call.
|
| - // Return true if the argument type check was added.
|
| - bool checkArgument(HInvokeDynamic instruction) {
|
| - // We want the right error in checked mode.
|
| - if (compiler.enableTypeAssertions) return false;
|
| - HInstruction left = instruction.inputs[1];
|
| - HInstruction right = instruction.inputs[2];
|
| -
|
| - Selector selector = instruction.selector;
|
| - if (selector.isOperator && left.isNumber(compiler)) {
|
| - if (right.isNumber(compiler)) return false;
|
| - TypeMask type = right.isIntegerOrNull(compiler)
|
| - ? right.instructionType.nonNullable()
|
| - : backend.numType;
|
| - // TODO(ngeoffray): Some number operations don't have a builtin
|
| - // variant and will do the check in their method anyway. We
|
| - // still add a check because it allows to GVN these operations,
|
| - // but we should find a better way.
|
| - convertInput(instruction,
|
| - right,
|
| - type,
|
| - HTypeConversion.ARGUMENT_TYPE_CHECK);
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - void processPendingOptimizations() {
|
| - pendingOptimizations.forEach((instruction, action) => action());
|
| - pendingOptimizations.clear();
|
| - }
|
| -
|
| - void addDependentInstructionsToWorkList(HInstruction instruction) {
|
| - for (int i = 0, length = instruction.usedBy.length; i < length; i++) {
|
| - // The type propagator only propagates types forward. We
|
| - // thus only need to add the users of the [instruction] to the list.
|
| - addToWorkList(instruction.usedBy[i]);
|
| - }
|
| - }
|
| -
|
| - void addAllUsersBut(HInvokeDynamic invoke, HInstruction instruction) {
|
| - instruction.usedBy.forEach((HInstruction user) {
|
| - if (user != invoke) addToWorkList(user);
|
| - });
|
| - }
|
| -
|
| - TypeMask visitInvokeDynamic(HInvokeDynamic instruction) {
|
| - if (instruction.isInterceptedCall) {
|
| - // We cannot do the following optimization now, because we have
|
| - // to wait for the type propagation to be stable. The receiver
|
| - // of [instruction] might move from number to dynamic.
|
| - pendingOptimizations.putIfAbsent(instruction, () => () {
|
| - Selector selector = instruction.selector;
|
| - if (selector.isOperator && selector.name != '==') {
|
| - if (checkReceiver(instruction)) {
|
| - addAllUsersBut(instruction, instruction.inputs[1]);
|
| - }
|
| - if (!selector.isUnaryOperator && checkArgument(instruction)) {
|
| - addAllUsersBut(instruction, instruction.inputs[2]);
|
| - }
|
| - }
|
| - });
|
| - }
|
| -
|
| - HInstruction receiver = instruction.getDartReceiver(compiler);
|
| - TypeMask receiverType = receiver.instructionType;
|
| - Selector selector =
|
| - new TypedSelector(receiverType, instruction.selector, classWorld);
|
| - instruction.selector = selector;
|
| -
|
| - // Try to specialize the receiver after this call.
|
| - if (receiver.dominatedUsers(instruction).length != 1
|
| - && !selector.isClosureCall) {
|
| - TypeMask newType = compiler.world.allFunctions.receiverType(selector);
|
| - newType = newType.intersection(receiverType, classWorld);
|
| - var next = instruction.next;
|
| - if (next is HTypeKnown && next.checkedInput == receiver) {
|
| - // We already have refined [receiver]. We still update the
|
| - // type of the [HTypeKnown] instruction because it may have
|
| - // been refined with a correct type at the time, but
|
| - // incorrect now.
|
| - if (next.instructionType != newType) {
|
| - next.knownType = next.instructionType = newType;
|
| - addDependentInstructionsToWorkList(next);
|
| - }
|
| - } else if (newType != receiverType) {
|
| - // Insert a refinement node after the call and update all
|
| - // users dominated by the call to use that node instead of
|
| - // [receiver].
|
| - HTypeKnown converted =
|
| - new HTypeKnown.witnessed(newType, receiver, instruction);
|
| - instruction.block.addBefore(instruction.next, converted);
|
| - receiver.replaceAllUsersDominatedBy(converted.next, converted);
|
| - addDependentInstructionsToWorkList(converted);
|
| - }
|
| - }
|
| -
|
| - return instruction.specializer.computeTypeFromInputTypes(
|
| - instruction, compiler);
|
| - }
|
| -}
|
|
|