| Index: pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| index 0a2b4cef8060d3aa61435b707c5d6d69397d3fc1..fa48c8436cf40d289e944a71142155bc5d0e31e4 100644
|
| --- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| +++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| @@ -121,6 +121,24 @@ class ConstantPropagationLattice {
|
| typeSystem.isDefinitelyInt(value.type, allowNull: allowNull);
|
| }
|
|
|
| + bool isDefinitelyUint31(AbstractValue value,
|
| + {bool allowNull: false}) {
|
| + return value.isNothing ||
|
| + typeSystem.isDefinitelyUint31(value.type, allowNull: allowNull);
|
| + }
|
| +
|
| + bool isDefinitelyUint32(AbstractValue value,
|
| + {bool allowNull: false}) {
|
| + return value.isNothing ||
|
| + typeSystem.isDefinitelyUint32(value.type, allowNull: allowNull);
|
| + }
|
| +
|
| + bool isDefinitelyUint(AbstractValue value,
|
| + {bool allowNull: false}) {
|
| + return value.isNothing ||
|
| + typeSystem.isDefinitelyUint(value.type, allowNull: allowNull);
|
| + }
|
| +
|
| bool isDefinitelyNativeList(AbstractValue value,
|
| {bool allowNull: false}) {
|
| return value.isNothing ||
|
| @@ -153,6 +171,19 @@ class ConstantPropagationLattice {
|
| typeSystem.isDefinitelyIndexable(value.type, allowNull: allowNull);
|
| }
|
|
|
| + /// Returns `true` if [value] represents an int value that must be in the
|
| + /// inclusive range.
|
| + bool isDefinitelyIntInRange(AbstractValue value, {int min, int max}) {
|
| + if (value.isNothing) return true;
|
| + if (!isDefinitelyInt(value)) return false;
|
| + PrimitiveConstantValue constant = value.constant;
|
| + if (constant == null) return false;
|
| + if (!constant.isInt) return false;
|
| + if (min != null && constant.primitiveValue < min) return false;
|
| + if (max != null && constant.primitiveValue > max) return false;
|
| + return true;
|
| + }
|
| +
|
| /// Returns whether the given [value] is an instance of [type].
|
| ///
|
| /// Since [value] and [type] are not always known, [AbstractBool.Maybe] is
|
| @@ -236,40 +267,229 @@ class ConstantPropagationLattice {
|
| AbstractValue binaryOp(BinaryOperator operator,
|
| AbstractValue left,
|
| AbstractValue right) {
|
| + switch (operator.kind) {
|
| + case BinaryOperatorKind.ADD:
|
| + return addSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.SUB:
|
| + return subtractSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.MUL:
|
| + return multiplySpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.DIV:
|
| + return divideSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.IDIV:
|
| + return truncatingDivideSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.MOD:
|
| + return moduloSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.EQ:
|
| + return equalSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.AND:
|
| + return andSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.OR:
|
| + return orSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.XOR:
|
| + return xorSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.SHL:
|
| + return shiftLeftSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.SHR:
|
| + return shiftRightSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.LT:
|
| + return lessSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.LTEQ:
|
| + return lessEqualSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.GT:
|
| + return greaterSpecial(left, right);
|
| +
|
| + case BinaryOperatorKind.GTEQ:
|
| + return greaterEqualSpecial(left, right);
|
| +
|
| + default:
|
| + break;
|
| + }
|
| +
|
| if (left.isNothing || right.isNothing) {
|
| return nothing;
|
| }
|
| if (left.isConstant && right.isConstant) {
|
| BinaryOperation operation = constantSystem.lookupBinary(operator);
|
| ConstantValue result = operation.fold(left.constant, right.constant);
|
| - if (result == null) return anything;
|
| - return constant(result);
|
| + if (result != null) return constant(result);
|
| }
|
| - // TODO(asgerf): Handle remaining operators and the UIntXX types.
|
| - switch (operator.kind) {
|
| - case BinaryOperatorKind.ADD:
|
| - case BinaryOperatorKind.SUB:
|
| - case BinaryOperatorKind.MUL:
|
| - if (isDefinitelyInt(left) && isDefinitelyInt(right)) {
|
| - return nonConstant(typeSystem.intType);
|
| - }
|
| - return null;
|
| + return null; // The caller will use return type from type inference.
|
| + }
|
|
|
| - case BinaryOperatorKind.EQ:
|
| - bool behavesLikeIdentity =
|
| - isDefinitelyNumStringBool(left, allowNull: true) ||
|
| - right.isNullConstant;
|
| - if (behavesLikeIdentity &&
|
| - typeSystem.areDisjoint(left.type, right.type)) {
|
| - return constant(new FalseConstantValue());
|
| - }
|
| - return null;
|
| + AbstractValue foldBinary(BinaryOperation operation,
|
| + AbstractValue left, AbstractValue right) {
|
| + if (left.isNothing || right.isNothing) return nothing;
|
| + if (left.isConstant && right.isConstant) {
|
| + ConstantValue result = operation.fold(left.constant, right.constant);
|
| + if (result != null) return constant(result);
|
| + }
|
| + return null;
|
| + }
|
|
|
| - default:
|
| - return null; // The caller will use return type from type inference.
|
| + AbstractValue closedOnInt(AbstractValue left, AbstractValue right) {
|
| + if (isDefinitelyInt(left) && isDefinitelyInt(right)) {
|
| + return nonConstant(typeSystem.intType);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + AbstractValue closedOnUint(AbstractValue left, AbstractValue right) {
|
| + if (isDefinitelyUint(left) && isDefinitelyUint(right)) {
|
| + return nonConstant(typeSystem.uintType);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + AbstractValue closedOnUint31(AbstractValue left, AbstractValue right) {
|
| + if (isDefinitelyUint31(left) && isDefinitelyUint31(right)) {
|
| + return nonConstant(typeSystem.uint31Type);
|
| }
|
| + return null;
|
| }
|
|
|
| + AbstractValue addSpecial(AbstractValue left, AbstractValue right) {
|
| + AbstractValue folded = foldBinary(constantSystem.add, left, right);
|
| + if (folded != null) return folded;
|
| + if (isDefinitelyNum(left)) {
|
| + if (isDefinitelyUint31(left) && isDefinitelyUint31(right)) {
|
| + return nonConstant(typeSystem.uint32Type);
|
| + }
|
| + return closedOnUint(left, right) ?? closedOnInt(left, right);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + AbstractValue subtractSpecial(AbstractValue left, AbstractValue right) {
|
| + AbstractValue folded = foldBinary(constantSystem.subtract, left, right);
|
| + return folded ?? closedOnInt(left, right);
|
| + }
|
| +
|
| + AbstractValue multiplySpecial(AbstractValue left, AbstractValue right) {
|
| + AbstractValue folded = foldBinary(constantSystem.multiply, left, right);
|
| + return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right);
|
| + }
|
| +
|
| + AbstractValue divideSpecial(AbstractValue left, AbstractValue right) {
|
| + return foldBinary(constantSystem.divide, left, right);
|
| + }
|
| +
|
| + AbstractValue truncatingDivideSpecial(
|
| + AbstractValue left, AbstractValue right) {
|
| + AbstractValue folded =
|
| + foldBinary(constantSystem.truncatingDivide, left, right);
|
| + if (folded != null) return folded;
|
| + if (isDefinitelyNum(left)) {
|
| + if (isDefinitelyUint32(left) && isDefinitelyIntInRange(right, min: 2)) {
|
| + return nonConstant(typeSystem.uint31Type);
|
| + }
|
| + if (isDefinitelyUint(right)) {
|
| + // `0` will be an exception, other values will shrink the result.
|
| + if (isDefinitelyUint31(left)) return nonConstant(typeSystem.uint31Type);
|
| + if (isDefinitelyUint32(left)) return nonConstant(typeSystem.uint32Type);
|
| + if (isDefinitelyUint(left)) return nonConstant(typeSystem.uintType);
|
| + }
|
| + return nonConstant(typeSystem.intType);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + AbstractValue moduloSpecial(AbstractValue left, AbstractValue right) {
|
| + AbstractValue folded = foldBinary(constantSystem.modulo, left, right);
|
| + return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right);
|
| + }
|
| +
|
| + AbstractValue remainderSpecial(AbstractValue left, AbstractValue right) {
|
| + if (left.isNothing || right.isNothing) return nothing;
|
| + AbstractValue folded = null; // Remainder not in constant system.
|
| + return folded ?? closedOnUint(left, right) ?? closedOnInt(left, right);
|
| + }
|
| +
|
| + AbstractValue equalSpecial(AbstractValue left, AbstractValue right) {
|
| + AbstractValue folded = foldBinary(constantSystem.equal, left, right);
|
| + if (folded != null) return folded;
|
| + bool behavesLikeIdentity =
|
| + isDefinitelyNumStringBool(left, allowNull: true) ||
|
| + right.isNullConstant;
|
| + if (behavesLikeIdentity &&
|
| + typeSystem.areDisjoint(left.type, right.type)) {
|
| + return constant(new FalseConstantValue());
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + AbstractValue andSpecial(AbstractValue left, AbstractValue right) {
|
| + AbstractValue folded = foldBinary(constantSystem.bitAnd, left, right);
|
| + if (folded != null) return folded;
|
| + if (isDefinitelyNum(left)) {
|
| + if (isDefinitelyUint31(left) || isDefinitelyUint31(right)) {
|
| + // Either 31-bit argument will truncate the other.
|
| + return nonConstant(typeSystem.uint31Type);
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + AbstractValue orSpecial(AbstractValue left, AbstractValue right) {
|
| + AbstractValue folded = foldBinary(constantSystem.bitOr, left, right);
|
| + return folded ?? closedOnUint31(left, right);
|
| + }
|
| +
|
| + AbstractValue xorSpecial(AbstractValue left, AbstractValue right) {
|
| + AbstractValue folded = foldBinary(constantSystem.bitXor, left, right);
|
| + return folded ?? closedOnUint31(left, right);
|
| + }
|
| +
|
| + AbstractValue shiftLeftSpecial(AbstractValue left, AbstractValue right) {
|
| + return foldBinary(constantSystem.shiftLeft, left, right);
|
| + }
|
| +
|
| + AbstractValue shiftRightSpecial(AbstractValue left, AbstractValue right) {
|
| + AbstractValue folded = foldBinary(constantSystem.shiftRight, left, right);
|
| + if (folded != null) return folded;
|
| + if (isDefinitelyUint31(left)) {
|
| + return nonConstant(typeSystem.uint31Type);
|
| + } else if (isDefinitelyUint32(left)) {
|
| + if (isDefinitelyIntInRange(right, min: 1, max: 31)) {
|
| + // A zero will be shifted into the 'sign' bit.
|
| + return nonConstant(typeSystem.uint31Type);
|
| + }
|
| + return nonConstant(typeSystem.uint32Type);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + AbstractValue lessSpecial(AbstractValue left, AbstractValue right) {
|
| + return foldBinary(constantSystem.less, left, right);
|
| + }
|
| +
|
| + AbstractValue lessEqualSpecial(AbstractValue left, AbstractValue right) {
|
| + return foldBinary(constantSystem.lessEqual, left, right);
|
| + }
|
| +
|
| + AbstractValue greaterSpecial(AbstractValue left, AbstractValue right) {
|
| + return foldBinary(constantSystem.greater, left, right);
|
| + }
|
| +
|
| + AbstractValue greaterEqualSpecial(AbstractValue left, AbstractValue right) {
|
| + return foldBinary(constantSystem.greaterEqual, left, right);
|
| + }
|
| +
|
| +
|
| AbstractValue stringConstant(String value) {
|
| return constant(new StringConstantValue(new ast.DartString.literal(value)));
|
| }
|
| @@ -395,6 +615,7 @@ final Map<String, BuiltinOperator> NumBinaryBuiltins =
|
| '+': BuiltinOperator.NumAdd,
|
| '-': BuiltinOperator.NumSubtract,
|
| '*': BuiltinOperator.NumMultiply,
|
| + '/': BuiltinOperator.NumDivide,
|
| '&': BuiltinOperator.NumAnd,
|
| '|': BuiltinOperator.NumOr,
|
| '^': BuiltinOperator.NumXor,
|
| @@ -769,21 +990,39 @@ class TransformingVisitor extends LeafVisitor {
|
| if (operator != null) {
|
| return replaceWithBinary(operator, leftArg, rightArg);
|
| }
|
| - // Try to insert a shift-left operator.
|
| // Shift operators are not in [NumBinaryBuiltins] because Dart shifts
|
| - // behave different than JS shifts.
|
| - // We do not introduce shift-right operators yet because the operator
|
| - // to use depends on whether the left-hand operand is negative.
|
| - // See js_number.dart in js_runtime for details.
|
| - PrimitiveConstantValue rightConstant = right.constant;
|
| + // behave different to JS shifts, especially in the handling of the
|
| + // shift count.
|
| + // Try to insert a shift-left operator.
|
| if (opname == '<<' &&
|
| lattice.isDefinitelyInt(left) &&
|
| - rightConstant != null &&
|
| - rightConstant.isInt &&
|
| - rightConstant.primitiveValue >= 0 &&
|
| - rightConstant.primitiveValue <= 31) {
|
| - return replaceWithBinary(BuiltinOperator.NumShl,
|
| - leftArg, rightArg);
|
| + lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) {
|
| + return replaceWithBinary(BuiltinOperator.NumShl, leftArg, rightArg);
|
| + }
|
| + // Try to insert a shift-right operator. JavaScript's right shift is
|
| + // consistent with Dart's only for left operands in the unsigned
|
| + // 32-bit range.
|
| + if (opname == '>>' &&
|
| + lattice.isDefinitelyUint32(left) &&
|
| + lattice.isDefinitelyIntInRange(right, min: 0, max: 31)) {
|
| + return replaceWithBinary(BuiltinOperator.NumShr, leftArg, rightArg);
|
| + }
|
| + // Try to use remainder for '%'. Both operands must be non-negative
|
| + // and the divisor must be non-zero.
|
| + if (opname == '%' &&
|
| + lattice.isDefinitelyUint(left) &&
|
| + lattice.isDefinitelyUint(right) &&
|
| + lattice.isDefinitelyIntInRange(right, min: 1)) {
|
| + return replaceWithBinary(
|
| + BuiltinOperator.NumRemainder, leftArg, rightArg);
|
| + }
|
| +
|
| + if (opname == '~/' &&
|
| + lattice.isDefinitelyUint32(left) &&
|
| + lattice.isDefinitelyIntInRange(right, min: 2)) {
|
| + return replaceWithBinary(
|
| + BuiltinOperator.NumTruncatingDivideToSigned32,
|
| + leftArg, rightArg);
|
| }
|
| }
|
| if (lattice.isDefinitelyString(left, allowNull: false) &&
|
| @@ -794,11 +1033,34 @@ class TransformingVisitor extends LeafVisitor {
|
| }
|
| }
|
| }
|
| + if (node.selector.isCall) {
|
| + String name = node.selector.name;
|
| + Primitive receiver = getDartReceiver(node);
|
| + AbstractValue receiverValue = getValue(receiver);
|
| + if (name == 'remainder') {
|
| + if (node.arguments.length == 2) {
|
| + Primitive arg = getDartArgument(node, 0);
|
| + AbstractValue argValue = getValue(arg);
|
| + if (lattice.isDefinitelyInt(receiverValue) &&
|
| + lattice.isDefinitelyInt(argValue) &&
|
| + isIntNotZero(argValue)) {
|
| + return
|
| + replaceWithBinary(BuiltinOperator.NumRemainder, receiver, arg);
|
| + }
|
| + }
|
| + }
|
| + }
|
| // We should only get here if the node was not specialized.
|
| assert(node.parent != null);
|
| return false;
|
| }
|
|
|
| + /// Returns `true` if [value] represents an int value that cannot be zero.
|
| + bool isIntNotZero(AbstractValue value) {
|
| + return lattice.isDefinitelyIntInRange(value, min: 1) ||
|
| + lattice.isDefinitelyIntInRange(value, max: -1);
|
| + }
|
| +
|
| bool isInterceptedSelector(Selector selector) {
|
| return backend.isInterceptedSelector(selector);
|
| }
|
| @@ -2223,6 +2485,30 @@ class TypePropagationVisitor implements Visitor {
|
| }
|
|
|
| void visitApplyBuiltinOperator(ApplyBuiltinOperator node) {
|
| +
|
| + void binaryOp(
|
| + AbstractValue operation(AbstractValue left, AbstractValue right),
|
| + TypeMask defaultType) {
|
| + AbstractValue left = getValue(node.arguments[0].definition);
|
| + AbstractValue right = getValue(node.arguments[1].definition);
|
| + setValue(node, operation(left, right) ?? nonConstant(defaultType));
|
| + }
|
| +
|
| + void binaryNumOp(
|
| + AbstractValue operation(AbstractValue left, AbstractValue right)) {
|
| + binaryOp(operation, typeSystem.numType);
|
| + }
|
| +
|
| + void binaryUint32Op(
|
| + AbstractValue operation(AbstractValue left, AbstractValue right)) {
|
| + binaryOp(operation, typeSystem.uint32Type);
|
| + }
|
| +
|
| + void binaryBoolOp(
|
| + AbstractValue operation(AbstractValue left, AbstractValue right)) {
|
| + binaryOp(operation, typeSystem.boolType);
|
| + }
|
| +
|
| switch (node.operator) {
|
| case BuiltinOperator.StringConcatenate:
|
| ast.DartString stringValue = const ast.LiteralDartString('');
|
| @@ -2272,6 +2558,7 @@ class TypePropagationVisitor implements Visitor {
|
| assert(leftConst.isConstant && rightConst.isConstant);
|
| PrimitiveConstantValue left = leftValue;
|
| PrimitiveConstantValue right = rightValue;
|
| + // Should this be constantSystem.identity.fold(left, right)?
|
| ConstantValue result =
|
| new BoolConstantValue(left.primitiveValue == right.primitiveValue);
|
| setValue(node, constantValue(result, typeSystem.boolType));
|
| @@ -2280,27 +2567,66 @@ class TypePropagationVisitor implements Visitor {
|
| }
|
| break;
|
|
|
| - // TODO(asgerf): Implement constant propagation for builtins.
|
| case BuiltinOperator.NumAdd:
|
| + binaryNumOp(lattice.addSpecial);
|
| + break;
|
| +
|
| case BuiltinOperator.NumSubtract:
|
| + binaryNumOp(lattice.subtractSpecial);
|
| + break;
|
| +
|
| case BuiltinOperator.NumMultiply:
|
| + binaryNumOp(lattice.multiplySpecial);
|
| + break;
|
| +
|
| + case BuiltinOperator.NumDivide:
|
| + binaryNumOp(lattice.divideSpecial);
|
| + break;
|
| +
|
| + case BuiltinOperator.NumRemainder:
|
| + binaryNumOp(lattice.remainderSpecial);
|
| + break;
|
| +
|
| + case BuiltinOperator.NumTruncatingDivideToSigned32:
|
| + binaryNumOp(lattice.truncatingDivideSpecial);
|
| + break;
|
| +
|
| case BuiltinOperator.NumAnd:
|
| + binaryUint32Op(lattice.andSpecial);
|
| + break;
|
| +
|
| case BuiltinOperator.NumOr:
|
| + binaryUint32Op(lattice.orSpecial);
|
| + break;
|
| +
|
| case BuiltinOperator.NumXor:
|
| + binaryUint32Op(lattice.xorSpecial);
|
| + break;
|
| +
|
| case BuiltinOperator.NumShl:
|
| - AbstractValue left = getValue(node.arguments[0].definition);
|
| - AbstractValue right = getValue(node.arguments[1].definition);
|
| - if (lattice.isDefinitelyInt(left) && lattice.isDefinitelyInt(right)) {
|
| - setValue(node, nonConstant(typeSystem.intType));
|
| - } else {
|
| - setValue(node, nonConstant(typeSystem.numType));
|
| - }
|
| + binaryUint32Op(lattice.shiftLeftSpecial);
|
| + break;
|
| +
|
| + case BuiltinOperator.NumShr:
|
| + binaryUint32Op(lattice.shiftRightSpecial);
|
| break;
|
|
|
| case BuiltinOperator.NumLt:
|
| + binaryBoolOp(lattice.lessSpecial);
|
| + break;
|
| +
|
| case BuiltinOperator.NumLe:
|
| + binaryBoolOp(lattice.lessEqualSpecial);
|
| + break;
|
| +
|
| case BuiltinOperator.NumGt:
|
| + binaryBoolOp(lattice.greaterSpecial);
|
| + break;
|
| +
|
| case BuiltinOperator.NumGe:
|
| + binaryBoolOp(lattice.greaterEqualSpecial);
|
| + break;
|
| +
|
| case BuiltinOperator.StrictNeq:
|
| case BuiltinOperator.LooseNeq:
|
| case BuiltinOperator.IsFalsy:
|
| @@ -2375,7 +2701,7 @@ class TypePropagationVisitor implements Visitor {
|
|
|
| void visitTypeTestViaFlag(TypeTestViaFlag node) {
|
| // TODO(sra): We could see if we can find the value in the interceptor
|
| - // expression. It would p[robably have no benefit - we only see
|
| + // expression. It would probably have no benefit - we only see
|
| // TypeTestViaFlag after rewriting TypeTest and the rewrite of TypeTest
|
| // would already have done the interesting optimizations.
|
| setValue(node, nonConstant(typeSystem.boolType));
|
|
|