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)); |