| Index: sdk/lib/_internal/compiler/implementation/ssa/nodes.dart
|
| ===================================================================
|
| --- sdk/lib/_internal/compiler/implementation/ssa/nodes.dart (revision 17016)
|
| +++ sdk/lib/_internal/compiler/implementation/ssa/nodes.dart (working copy)
|
| @@ -261,7 +261,7 @@
|
| visitInvokeBinary(HInvokeBinary node) => visitInvokeStatic(node);
|
| visitInvokeDynamic(HInvokeDynamic node) => visitInvoke(node);
|
| visitInvokeDynamicField(HInvokeDynamicField node) => visitInvokeDynamic(node);
|
| - visitInvokeUnary(HInvokeUnary node) => visitInvokeStatic(node);
|
| + visitInvokeUnary(HInvokeUnary node) => visitInstruction(node);
|
| visitConditionalBranch(HConditionalBranch node) => visitControlFlow(node);
|
| visitControlFlow(HControlFlow node) => visitInstruction(node);
|
| visitFieldAccess(HFieldAccess node) => visitInstruction(node);
|
| @@ -293,7 +293,7 @@
|
| visitIdentity(HIdentity node) => visitRelational(node);
|
| visitIf(HIf node) => visitConditionalBranch(node);
|
| visitIndex(HIndex node) => visitInstruction(node);
|
| - visitIndexAssign(HIndexAssign node) => visitInvokeStatic(node);
|
| + visitIndexAssign(HIndexAssign node) => visitInstruction(node);
|
| visitIntegerCheck(HIntegerCheck node) => visitCheck(node);
|
| visitInterceptor(HInterceptor node) => visitInstruction(node);
|
| visitInvokeClosure(HInvokeClosure node)
|
| @@ -1329,6 +1329,7 @@
|
| bool isIndexOperatorOnIndexablePrimitive(HTypeMap types) {
|
| return isInterceptorCall
|
| && selector.kind == SelectorKind.INDEX
|
| + && selector.name == const SourceString('[]')
|
| && inputs[1].isIndexablePrimitive(types);
|
| }
|
|
|
| @@ -1337,20 +1338,57 @@
|
| Compiler compiler) {
|
| // TODO(ngeoffray): Move this logic into a different class that
|
| // will know what type it wants for a given selector.
|
| - if (selector.kind != SelectorKind.INDEX) return HType.UNKNOWN;
|
| if (!isInterceptorCall) return HType.UNKNOWN;
|
|
|
| - HInstruction index = inputs[2];
|
| - if (input == inputs[1] &&
|
| - (index.isTypeUnknown(types) || index.isNumber(types))) {
|
| - return HType.INDEXABLE_PRIMITIVE;
|
| + if (selector.kind == SelectorKind.INDEX) {
|
| + HInstruction index = inputs[2];
|
| + if (input == inputs[1] &&
|
| + (index.isTypeUnknown(types) || index.isNumber(types))) {
|
| + return selector.name == const SourceString('[]')
|
| + ? HType.INDEXABLE_PRIMITIVE
|
| + : HType.MUTABLE_ARRAY;
|
| + }
|
| + // The index should be an int when the receiver is a string or array.
|
| + // However it turns out that inserting an integer check in the optimized
|
| + // version is cheaper than having another bailout case. This is true,
|
| + // because the integer check will simply throw if it fails.
|
| + return HType.UNKNOWN;
|
| + } else if (selector.kind == SelectorKind.OPERATOR) {
|
| + HType propagatedType = types[this];
|
| + if (selector.name == const SourceString('-') && input == inputs[1]) {
|
| + // If the outgoing type should be a number (integer, double or both) we
|
| + // want the outgoing type to be the input too.
|
| + // If we don't know the outgoing type we try to make it a number.
|
| + if (propagatedType.isNumber()) return propagatedType;
|
| + if (propagatedType.isUnknown()) return HType.NUMBER;
|
| + } else if (selector.name == const SourceString('~')
|
| + && input == inputs[1]) {
|
| + if (propagatedType.isUnknown() || propagatedType.isNumber()) {
|
| + return HType.INTEGER;
|
| + }
|
| + }
|
| + return HType.UNKNOWN;
|
| }
|
| - // The index should be an int when the receiver is a string or array.
|
| - // However it turns out that inserting an integer check in the optimized
|
| - // version is cheaper than having another bailout case. This is true,
|
| - // because the integer check will simply throw if it fails.
|
| return HType.UNKNOWN;
|
| }
|
| +
|
| + HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
|
| + // TODO(ngeoffray): Move this logic into a different class that
|
| + // will know what type it has for a given selector.
|
| + if (!isInterceptorCall) return HType.UNKNOWN;
|
| +
|
| + if (selector.kind == SelectorKind.OPERATOR) {
|
| + if (selector.name == const SourceString('-')) {
|
| + HType operandType = types[inputs[1]];
|
| + if (operandType.isNumber()) return operandType;
|
| + } else if (selector.name == const SourceString('~')) {
|
| + // All bitwise operations on primitive types either produce an
|
| + // integer or throw an error.
|
| + if (inputs[1].isPrimitive(types)) return HType.INTEGER;
|
| + }
|
| + }
|
| + return HType.UNKNOWN;
|
| + }
|
| }
|
|
|
| abstract class HInvokeDynamicField extends HInvokeDynamic {
|
| @@ -1840,53 +1878,27 @@
|
| bool dataEquals(HInstruction other) => true;
|
| }
|
|
|
| -abstract class HInvokeUnary extends HInvokeStatic {
|
| - HInvokeUnary(HStatic target, HInstruction input)
|
| - : super(<HInstruction>[target, input]);
|
| +abstract class HInvokeUnary extends HInstruction {
|
| + HInvokeUnary(HInstruction input) : super(<HInstruction>[input]);
|
|
|
| - HInstruction get operand => inputs[1];
|
| + HInstruction get operand => inputs[0];
|
|
|
| void prepareGvn(HTypeMap types) {
|
| clearAllSideEffects();
|
| - // A unary arithmetic expression can take part in global value
|
| - // numbering and does not have any side-effects if its input is a
|
| - // number.
|
| - if (isBuiltin(types)) {
|
| - setUseGvn();
|
| - } else {
|
| - setAllSideEffects();
|
| - }
|
| + setUseGvn();
|
| }
|
|
|
| - bool isBuiltin(HTypeMap types) => operand.isNumber(types);
|
| -
|
| - HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
|
| - HType operandType = types[operand];
|
| - if (operandType.isNumber()) return operandType;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - HType computeDesiredTypeForNonTargetInput(HInstruction input,
|
| - HTypeMap types,
|
| - Compiler compiler) {
|
| - HType propagatedType = types[this];
|
| - // If the outgoing type should be a number (integer, double or both) we
|
| - // want the outgoing type to be the input too.
|
| - // If we don't know the outgoing type we try to make it a number.
|
| - if (propagatedType.isNumber()) return propagatedType;
|
| - if (propagatedType.isUnknown()) return HType.NUMBER;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - HType computeLikelyType(HTypeMap types, Compiler compiler) => HType.NUMBER;
|
| -
|
| UnaryOperation operation(ConstantSystem constantSystem);
|
| }
|
|
|
| class HNegate extends HInvokeUnary {
|
| - HNegate(HStatic target, HInstruction input) : super(target, input);
|
| + HNegate(HInstruction input) : super(input);
|
| accept(HVisitor visitor) => visitor.visitNegate(this);
|
|
|
| + HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
|
| + return types[operand];
|
| + }
|
| +
|
| UnaryOperation operation(ConstantSystem constantSystem)
|
| => constantSystem.negate;
|
| int typeCode() => HInstruction.NEGATE_TYPECODE;
|
| @@ -1895,28 +1907,10 @@
|
| }
|
|
|
| class HBitNot extends HInvokeUnary {
|
| - HBitNot(HStatic target, HInstruction input) : super(target, input);
|
| + HBitNot(HInstruction input) : super(input);
|
| accept(HVisitor visitor) => visitor.visitBitNot(this);
|
| -
|
| - HType computeTypeFromInputTypes(HTypeMap types, Compiler compiler) {
|
| - // All bitwise operations on primitive types either produce an
|
| - // integer or throw an error.
|
| - if (operand.isPrimitive(types)) return HType.INTEGER;
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - HType computeDesiredTypeForNonTargetInput(HInstruction input,
|
| - HTypeMap types,
|
| - Compiler compiler) {
|
| - HType propagatedType = types[this];
|
| - // Bit operations only work on integers. If there is no desired output
|
| - // type or if it as a number we want to get an integer as input.
|
| - if (propagatedType.isUnknown() || propagatedType.isNumber()) {
|
| - return HType.INTEGER;
|
| - }
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| +
|
| + HType get guaranteedType => HType.INTEGER;
|
| UnaryOperation operation(ConstantSystem constantSystem)
|
| => constantSystem.bitNot;
|
| int typeCode() => HInstruction.BIT_NOT_TYPECODE;
|
| @@ -2485,48 +2479,22 @@
|
| bool dataEquals(HIndex other) => true;
|
| }
|
|
|
| -class HIndexAssign extends HInvokeStatic {
|
| - HIndexAssign(HStatic target,
|
| - HInstruction receiver,
|
| +class HIndexAssign extends HInstruction {
|
| + HIndexAssign(HInstruction receiver,
|
| HInstruction index,
|
| HInstruction value)
|
| - : super(<HInstruction>[target, receiver, index, value]);
|
| - toString() => 'index assign operator';
|
| + : super(<HInstruction>[receiver, index, value]);
|
| + String toString() => 'index assign operator';
|
| accept(HVisitor visitor) => visitor.visitIndexAssign(this);
|
|
|
| - HInstruction get receiver => inputs[1];
|
| - HInstruction get index => inputs[2];
|
| - HInstruction get value => inputs[3];
|
| + HInstruction get receiver => inputs[0];
|
| + HInstruction get index => inputs[1];
|
| + HInstruction get value => inputs[2];
|
|
|
| void prepareGvn(HTypeMap types) {
|
| clearAllSideEffects();
|
| - if (isBuiltin(types)) {
|
| - setChangesIndex();
|
| - } else {
|
| - setAllSideEffects();
|
| - }
|
| + setChangesIndex();
|
| }
|
| -
|
| - // Note, that we don't have a computeTypeFromInputTypes, since [HIndexAssign]
|
| - // is never used as input.
|
| -
|
| - HType computeDesiredTypeForNonTargetInput(HInstruction input,
|
| - HTypeMap types,
|
| - Compiler compiler) {
|
| - if (input == receiver &&
|
| - (index.isTypeUnknown(types) || index.isNumber(types))) {
|
| - return HType.MUTABLE_ARRAY;
|
| - }
|
| - // The index should be an int when the receiver is a string or array.
|
| - // However it turns out that inserting an integer check in the optimized
|
| - // version is cheaper than having another bailout case. This is true,
|
| - // because the integer check will simply throw if it fails.
|
| - return HType.UNKNOWN;
|
| - }
|
| -
|
| - bool isBuiltin(HTypeMap types)
|
| - => receiver.isMutableArray(types) && index.isInteger(types);
|
| - bool isJsStatement(HTypeMap types) => !isBuiltin(types);
|
| }
|
|
|
| class HIs extends HInstruction {
|
|
|