Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(323)

Unified Diff: lib/compiler/implementation/ssa/nodes.dart

Issue 10098001: Refactor type propagation. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix type for null and update tests. Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: lib/compiler/implementation/ssa/nodes.dart
diff --git a/lib/compiler/implementation/ssa/nodes.dart b/lib/compiler/implementation/ssa/nodes.dart
index 43e19ffc4ef6f394b57c69f82497bd927ed1a65c..bf0584b95720a503e18bcc91d83e760f409636ca 100644
--- a/lib/compiler/implementation/ssa/nodes.dart
+++ b/lib/compiler/implementation/ssa/nodes.dart
@@ -786,7 +786,8 @@ class HType {
bool isNumber() => (this.flag & (FLAG_INTEGER | FLAG_DOUBLE)) != 0;
bool isStringOrArray() =>
(this.flag & (FLAG_STRING | FLAG_READABLE_ARRAY)) != 0;
- bool isKnown() => this !== UNKNOWN && this !== CONFLICTING;
+ /** A type is useful it is not unknown and not conflicting. */
+ bool isUseful() => this !== UNKNOWN && this !== CONFLICTING;
static HType getTypeFromFlag(int flag) {
if (flag === CONFLICTING.flag) return CONFLICTING;
@@ -834,7 +835,6 @@ class HInstruction implements Hashable {
HInstruction previous = null;
HInstruction next = null;
int flags = 0;
- HType type = HType.UNKNOWN;
// Changes flags.
static final int FLAG_CHANGES_SOMETHING = 0;
@@ -846,7 +846,11 @@ class HInstruction implements Hashable {
// Other flags.
static final int FLAG_USE_GVN = FLAG_DEPENDS_ON_SOMETHING + 1;
- HInstruction(this.inputs) : id = idCounter++, usedBy = <HInstruction>[];
+ HInstruction(this.inputs)
+ : id = idCounter++,
+ usedBy = <HInstruction>[] {
+ if (guaranteedType.isUseful()) propagatedType = guaranteedType;
+ }
int hashCode() => id;
@@ -868,24 +872,66 @@ class HInstruction implements Hashable {
// Does this node potentially affect control flow.
bool isControlFlow() => false;
- bool isArray() => type.isArray();
- bool isMutableArray() => type.isMutableArray();
- bool isBoolean() => type.isBoolean();
- bool isInteger() => type.isInteger();
- bool isNumber() => type.isNumber();
- bool isString() => type.isString();
- bool isTypeUnknown() => type.isUnknown();
- bool isStringOrArray() => type.isStringOrArray();
+ // All isFunctions work on the propagated types.
+ bool isArray() => propagatedType.isArray();
+ bool isMutableArray() => propagatedType.isMutableArray();
+ bool isBoolean() => propagatedType.isBoolean();
+ bool isInteger() => propagatedType.isInteger();
+ bool isDouble() => propagatedType.isDouble();
+ bool isNumber() => propagatedType.isNumber();
+ bool isString() => propagatedType.isString();
+ bool isTypeUnknown() => propagatedType.isUnknown();
+ bool isStringOrArray() => propagatedType.isStringOrArray();
- // Compute the type of the instruction.
- HType computeType() => HType.UNKNOWN;
+ /**
+ * This is the type the instruction is guaranteed to have. It does not
+ * take any propagation into account.
+ */
+ HType get guaranteedType() => HType.UNKNOWN;
+ bool hasGuaranteedType() => !guaranteedType.isUnknown();
- HType computeDesiredInputType(HInstruction input) => HType.UNKNOWN;
+ /**
+ * The [propagatedType] is computed from the propagated types of the
+ * instruction's inputs. It is not allowed to do any guess work and can not
+ * be conflicting.
+ *
+ * The type propagator may also use this field to set the desired type. In
+ * this case [computeTypeFromInputTypes()] and [propagatedType] may differ.
+ * If this bad type is used then the use must be guarded.
+ *
Lasse Reichstein Nielsen 2012/04/16 11:37:04 This is still confusing. The propagatedType is nam
floitsch 2012/04/16 19:59:37 Reworded the description: The propagated type is t
+ * Note that the [propagatedType] may be set to [HType.CONFLICTING].
+ */
+ HType propagatedType = HType.UNKNOWN;
- // Returns whether the instruction does produce the type it claims.
- // For most instructions, this returns false. A type guard will be
- // inserted to make sure the users get the right type in.
- bool hasExpectedType() => false;
+ /**
+ * Some instructions have a good idea of their return type, but cannot
+ * guarantee the type. The [likelyType] does not need to be more specialized
+ * than the [propagatedType].
+ *
+ * Examples: the [likelyType] of [:x == y:] is a boolean. In most cases this
+ * cannot be guaranteed, but when merging types we still want to use this
+ * information.
+ *
+ * Similarily the [HAdd] instruction is likely a number. Note that, even if
+ * the [propagatedType] is already set to integer, the [likelyType] still
+ * might just return the number type.
+ */
+ HType get likelyType() => propagatedType;
+
+ /**
+ * Compute the type of the instruction by propagating the input types through
+ * the instruction.
+ */
+ // By default just copy the guaranteed type.
kasperl 2012/04/16 09:09:43 Fold this comment into the /** doc */ comment?
Lasse Reichstein Nielsen 2012/04/16 11:37:04 Or into a proper method body, instead of using an
floitsch 2012/04/16 19:59:37 Done.
+ HType computeTypeFromInputTypes() => guaranteedType;
+
+ /**
+ * Compute the desired type for the the given [input]. Aside from using
+ * other inputs to compute the desired type one should also use
+ * the [propagatedType] which, during the invocation of this method,
+ * represents the desired type of [this].
+ */
+ HType computeDesiredTypeForInput(HInstruction input) => HType.UNKNOWN;
bool isInBasicBlock() => block !== null;
@@ -1000,8 +1046,7 @@ class HBoolify extends HInstruction {
setUseGvn();
}
- HType computeType() => HType.BOOLEAN;
- bool hasExpectedType() => true;
+ HType get guaranteedType() => HType.BOOLEAN;
accept(HVisitor visitor) => visitor.visitBoolify(this);
int typeCode() => 0;
@@ -1029,21 +1074,16 @@ class HTypeGuard extends HInstruction {
HInstruction get guarded() => inputs.last();
- HType computeType() => type;
- bool hasExpectedType() => true;
-
bool isControlFlow() => true;
accept(HVisitor visitor) => visitor.visitTypeGuard(this);
int typeCode() => 1;
bool typeEquals(other) => other is HTypeGuard;
- bool dataEquals(HTypeGuard other) => type == other.type;
+ bool dataEquals(HTypeGuard other) => propagatedType == other.propagatedType;
}
class HBoundsCheck extends HCheck {
- HBoundsCheck(length, index) : super(<HInstruction>[length, index]) {
- type = HType.INTEGER;
- }
+ HBoundsCheck(length, index) : super(<HInstruction>[length, index]);
HInstruction get length() => inputs[0];
HInstruction get index() => inputs[1];
@@ -1053,8 +1093,7 @@ class HBoundsCheck extends HCheck {
setUseGvn();
}
- HType computeType() => HType.INTEGER;
- bool hasExpectedType() => true;
+ HType get guaranteedType() => HType.INTEGER;
accept(HVisitor visitor) => visitor.visitBoundsCheck(this);
int typeCode() => 2;
@@ -1072,8 +1111,7 @@ class HIntegerCheck extends HCheck {
setUseGvn();
}
- HType computeType() => HType.INTEGER;
- bool hasExpectedType() => true;
+ HType get guaranteedType() => HType.INTEGER;
accept(HVisitor visitor) => visitor.visitIntegerCheck(this);
int typeCode() => 3;
@@ -1178,7 +1216,7 @@ class HInvokeStatic extends HInvoke {
&& element.enclosingElement.name.slowToString() == 'List');
}
- HType computeType() {
+ HType get guaranteedType() {
if (isArrayConstructor()) {
return HType.MUTABLE_ARRAY;
}
@@ -1186,7 +1224,6 @@ class HInvokeStatic extends HInvoke {
}
bool get builtin() => isArrayConstructor();
- bool hasExpectedType() => isArrayConstructor();
}
class HInvokeSuper extends HInvokeStatic {
@@ -1207,10 +1244,14 @@ class HInvokeInterceptor extends HInvokeStatic {
toString() => 'invoke interceptor: ${element.name}';
accept(HVisitor visitor) => visitor.visitInvokeInterceptor(this);
- String get builtinJsName() {
- if (getter
+ bool isLengthGetterOnStringOrArray() {
+ return getter
&& name == const SourceString('length')
- && inputs[1].isStringOrArray()) {
+ && inputs[1].isStringOrArray();
+ }
+
+ String get builtinJsName() {
+ if (isLengthGetterOnStringOrArray()) {
return 'length';
} else if (name == const SourceString('add')
&& inputs[1].isMutableArray()) {
@@ -1222,17 +1263,24 @@ class HInvokeInterceptor extends HInvokeStatic {
return null;
}
- HType computeType() {
- if (getter
- && name == const SourceString('length')
- && inputs[1].isStringOrArray()) {
+ HType get guaranteedType() => HType.UNKNOWN;
+
+ HType get likelyType() {
+ // In general a length getter or method returns an int.
+ if (name == const SourceString('length')) return HType.INTEGER;
+ return type;
+ }
+
+ HType computeTypeFromInputTypes() {
+ if (isLengthGetterOnStringOrArray()) {
return HType.INTEGER;
}
return HType.UNKNOWN;
}
- HType computeDesiredInputType(HInstruction input) {
- if (input == inputs[0]) return HType.UNKNOWN;
+ HType computeDesiredTypeForInput(HInstruction input) {
+ // TODO(floitsch): we want the target to be a function.
+ if (input == target) return HType.UNKNOWN;
if (input == inputs[1] && input.isStringOrArray()) {
if (name == const SourceString('add')
|| name == const SourceString('removeLast')) {
@@ -1242,10 +1290,8 @@ class HInvokeInterceptor extends HInvokeStatic {
return HType.UNKNOWN;
}
- bool hasExpectedType() => builtinJsName != null;
-
void prepareGvn() {
- if (builtinJsName == 'length') {
+ if (isLengthGetterOnStringOrArray()) {
clearAllSideEffects();
} else {
setAllSideEffects();
@@ -1288,12 +1334,13 @@ class HFieldSet extends HInstruction {
class HForeign extends HInstruction {
final DartString code;
- final DartString declaredType;
- HForeign(this.code, this.declaredType, List<HInstruction> inputs)
- : super(inputs);
+ final HType foreignType;
+ HForeign(this.code, DartString declaredType, List<HInstruction> inputs)
+ : foreignType = computeTypeFromDeclaredType(declaredType),
+ super(inputs);
accept(HVisitor visitor) => visitor.visitForeign(this);
- HType computeType() {
+ static HType computeTypeFromDeclaredType(DartString declaredType) {
if (declaredType.slowToString() == 'bool') return HType.BOOLEAN;
if (declaredType.slowToString() == 'int') return HType.INTEGER;
if (declaredType.slowToString() == 'num') return HType.NUMBER;
@@ -1301,7 +1348,7 @@ class HForeign extends HInstruction {
return HType.UNKNOWN;
}
- bool hasExpectedType() => true;
+ HType get guaranteedType() => foreignType;
}
class HForeignNew extends HForeign {
@@ -1319,15 +1366,6 @@ class HInvokeBinary extends HInvokeStatic {
HInstruction get left() => inputs[1];
HInstruction get right() => inputs[2];
- HType computeInputsType() {
- HType leftType = left.type;
- HType rightType = right.type;
- if (leftType.isUnknown() || rightType.isUnknown()) {
- return HType.UNKNOWN;
- }
- return leftType.combine(rightType);
- }
-
abstract BinaryOperation get operation();
}
@@ -1349,22 +1387,32 @@ class HBinaryArithmetic extends HInvokeBinary {
bool get builtin() => left.isNumber() && right.isNumber();
- HType computeType() {
- HType inputsType = computeInputsType();
- if (inputsType.isKnown()) return inputsType;
- if (left.isNumber()) return HType.NUMBER;
+ HType computeTypeFromInputTypes() {
+ if (left.isInteger() && right.isInteger()) return left.propagatedType;
+ if (left.isNumber()) {
+ if (left.isDouble() || right.isDouble()) return HType.DOUBLE;
+ return HType.NUMBER;
+ }
return HType.UNKNOWN;
}
- HType computeDesiredInputType(HInstruction input) {
+ HType computeDesiredTypeForInput(HInstruction input) {
// TODO(floitsch): we want the target to be a function.
if (input == target) return HType.UNKNOWN;
- if (isNumber() || left.isNumber() || right.isNumber()) return HType.NUMBER;
- if (type.isUnknown()) return HType.NUMBER;
+ if (propagatedType.isInteger()) {
+ return HType.INTEGER;
+ }
+ if (propagatedType.isUnknown() || propagatedType.isNumber()) {
+ return HType.NUMBER;
+ }
+ return HType.UNKNOWN;
+ }
+
+ HType get likelyType() {
+ if (left.isTypeUnknown()) return HType.NUMBER;
return HType.UNKNOWN;
}
- bool hasExpectedType() => left.isNumber() && right.isNumber();
// TODO(1603): The class should be marked as abstract.
abstract BinaryOperation get operation();
}
@@ -1380,24 +1428,34 @@ class HAdd extends HBinaryArithmetic {
|| (left.isString() && right is HConstant);
}
- HType computeType() {
- HType computedType = computeInputsType();
- if (computedType.isConflicting() && left.isString()) return HType.STRING;
- if (computedType.isKnown()) return computedType;
- if (left.isNumber()) return HType.NUMBER;
+ HType computeTypeFromInputTypes() {
+ if (left.isInteger() && right.isInteger()) return left.propagatedType;
+ if (left.isNumber()) {
+ if (left.isDouble() || right.isDouble()) return HType.DOUBLE;
+ return HType.NUMBER;
+ }
+ if (left.isString()) return HType.STRING;
return HType.UNKNOWN;
}
- bool hasExpectedType() => builtin || type.isUnknown() || left.isString();
-
- HType computeDesiredInputType(HInstruction input) {
+ HType computeDesiredTypeForInput(HInstruction input) {
// TODO(floitsch): we want the target to be a function.
if (input == target) return HType.UNKNOWN;
- if (isString() || left.isString()) {
- return (input == left) ? HType.STRING : HType.UNKNOWN;
+ if (propagatedType.isInteger()) {
+ return HType.INTEGER;
}
- if (right.isString()) return HType.STRING;
- if (isNumber() || left.isNumber() || right.isNumber()) return HType.NUMBER;
+ if (propagatedType.isString() || left.isString() || right.isString()) {
+ return HType.STRING;
+ }
+ if (propagatedType.isNumber() || left.isNumber() || right.isNumber()) {
+ return HType.NUMBER;
+ }
+ return HType.UNKNOWN;
+ }
+
+ HType get likelyType() {
+ if (left.isString() || right.isString()) return HType.STRING;
+ if (left.isTypeUnknown() || left.isNumber()) return HType.NUMBER;
return HType.UNKNOWN;
}
@@ -1415,12 +1473,19 @@ class HDivide extends HBinaryArithmetic {
bool get builtin() => left.isNumber() && right.isNumber();
- HType computeType() {
- HType inputsType = computeInputsType();
+ HType computeTypeFromInputTypes() {
if (left.isNumber()) return HType.DOUBLE;
return HType.UNKNOWN;
}
+ HType computeDesiredTypeForInput(HInstruction input) {
+ // TODO(floitsch): we want the target to be a function.
+ if (input == target) return HType.UNKNOWN;
+ // A division can never return an integer. So don't ask for integer inputs.
+ if (propagatedType.isInteger()) return HType.UNKNOWN;
+ return super.computeDesiredTypeForInput(input);
+ }
+
DivideOperation get operation() => const DivideOperation();
int typeCode() => 6;
bool typeEquals(other) => other is HDivide;
@@ -1481,17 +1546,23 @@ class HBinaryBitOp extends HBinaryArithmetic {
bool get builtin() => left.isInteger() && right.isInteger();
- HType computeType() {
- HType inputsType = computeInputsType();
- if (inputsType.isKnown()) return inputsType;
+ HType computeTypeFromInputTypes() {
if (left.isInteger()) return HType.INTEGER;
return HType.UNKNOWN;
}
- HType computeDesiredInputType(HInstruction input) {
+ HType computeDesiredTypeForInput(HInstruction input) {
// TODO(floitsch): we want the target to be a function.
if (input == target) return HType.UNKNOWN;
- return HType.INTEGER;
+ if (propagatedType.isUnknown() || propagatedType.isNumber()) {
+ return HType.INTEGER;
+ }
+ return HType.UNKNOWN;
+ }
+
+ HType get likelyType() {
+ if (left.isTypeUnknown()) return HType.INTEGER;
+ return HType.UNKNOWN;
}
// TODO(floitsch): make class abstract instead of adding an abstract method.
@@ -1573,20 +1644,23 @@ class HInvokeUnary extends HInvokeStatic {
bool get builtin() => operand.isNumber();
- HType computeType() {
- HType operandType = operand.type;
- if (!operandType.isUnknown()) return operandType;
+ HType computeTypeFromInputTypes() {
+ HType operandType = operand.propagatedType;
+ if (operandType.isNumber()) return operandType;
return HType.UNKNOWN;
}
- HType computeDesiredInputType(HInstruction input) {
+ HType computeDesiredTypeForInput(HInstruction input) {
// TODO(floitsch): we want the target to be a function.
if (input == target) return HType.UNKNOWN;
- if (type.isUnknown() || type.isNumber()) return HType.NUMBER;
+ if (propagatedType.isInteger()) return HType.INTEGER;
+ if (propagatedType.isUnknown() || propagatedType.isNumber()) {
kasperl 2012/04/16 09:09:43 Maybe we should add more documentation to the comp
floitsch 2012/04/16 19:59:37 Added a lot of comments. I hope that's what you ha
+ return HType.NUMBER;
+ }
return HType.UNKNOWN;
}
- bool hasExpectedType() => builtin || type.isUnknown();
+ HType get likelyType() => HType.NUMBER;
abstract UnaryOperation get operation();
}
@@ -1607,16 +1681,19 @@ class HBitNot extends HInvokeUnary {
bool get builtin() => operand.isInteger();
- HType computeType() {
- HType operandType = operand.type;
- if (!operandType.isUnknown()) return operandType;
+ HType computeTypeFromInputTypes() {
+ HType operandType = operand.propagatedType;
+ if (operandType.isInteger()) return HType.INTEGER;
return HType.UNKNOWN;
}
- HType computeDesiredInputType(HInstruction input) {
+ HType computeDesiredTypeForInput(HInstruction input) {
// TODO(floitsch): we want the target to be a function.
if (input == target) return HType.UNKNOWN;
- return HType.INTEGER;
+ if (propagatedType.isUnknown() || propagatedType.isNumber()) {
+ return HType.INTEGER;
+ }
+ return HType.UNKNOWN;
}
BitNotOperation get operation() => const BitNotOperation();
@@ -1705,9 +1782,10 @@ class HLoopBranch extends HConditionalBranch {
class HConstant extends HInstruction {
final Constant constant;
- HConstant.internal(this.constant, HType type) : super(<HInstruction>[]) {
- this.type = type;
- }
+ final HType constantType;
+ HConstant.internal(this.constant, HType type)
kasperl 2012/04/16 09:09:43 this.constantType in the parameter list?
floitsch 2012/04/16 19:59:37 Done.
+ : this.constantType = type,
+ super(<HInstruction>[]);
void prepareGvn() {
assert(!hasSideEffects());
@@ -1715,9 +1793,8 @@ class HConstant extends HInstruction {
toString() => 'literal: $constant';
accept(HVisitor visitor) => visitor.visitConstant(this);
- HType computeType() => type;
- bool hasExpectedType() => true;
+ HType get guaranteedType() => constantType;
bool isConstant() => true;
bool isConstantBoolean() => constant.isBool();
@@ -1736,9 +1813,9 @@ class HNot extends HInstruction {
setUseGvn();
}
- HType computeType() => HType.BOOLEAN;
- bool hasExpectedType() => true;
- HType computeDesiredInputType(HInstruction input) {
+ HType get guaranteedType() => HType.BOOLEAN;
+
+ HType computeDesiredTypeForInput(HInstruction input) {
return HType.BOOLEAN;
}
@@ -1796,35 +1873,42 @@ class HPhi extends HInstruction {
// have the same known type return it. If any two inputs have
// different known types, we'll return a conflict -- otherwise we'll
// simply return an unknown type.
- HType computeInputsType() {
+ HType computeInputsType(bool unknownWins) {
bool seenUnknown = false;
- HType candidateType = inputs[0].type;
+ HType candidateType = inputs[0].propagatedType;
for (int i = 1, length = inputs.length; i < length; i++) {
- HType inputType = inputs[i].type;
- if (inputType.isUnknown()) return HType.UNKNOWN;
- candidateType = candidateType.combine(inputType);
- if (candidateType.isConflicting()) return HType.CONFLICTING;
+ HType inputType = inputs[i].propagatedType;
+ if (inputType.isUnknown()) {
+ seenUnknown = true;
+ } else {
+ candidateType = candidateType.combine(inputType);
+ if (candidateType.isConflicting()) return HType.CONFLICTING;
+ }
}
+ if (seenUnknown && unknownWins) return HType.UNKNOWN;
return candidateType;
}
- HType computeType() {
- HType inputsType = computeInputsType();
- if (!inputsType.isUnknown()) return inputsType;
- return super.computeType();
+ HType computeTypeFromInputTypes() {
+ HType inputsType = computeInputsType(true);
+ if (inputsType.isConflicting()) return HType.UNKNOWN;
+ return inputsType;
}
- HType computeDesiredInputType(HInstruction input) {
- if (type.isNumber()) return HType.NUMBER;
- if (type.isStringOrArray()) return HType.STRING_OR_ARRAY;
- return type;
+ HType computeDesiredTypeForInput(HInstruction input) {
+ if (propagatedType.isUnknown()) return likelyType;
+ if (propagatedType.isConflicting()) return HType.UNKNOWN;
+ return propagatedType;
}
- bool hasExpectedType() {
- for (int i = 0; i < inputs.length; i++) {
- if (type.combine(inputs[i].type).isConflicting()) return false;
- }
- return true;
+ HType get likelyType() {
+ HType agreedType = computeInputsType(false);
+ if (agreedType.isConflicting()) return HType.UNKNOWN;
+ // Don't be too restrictive. If the agreed type is integer or double just
+ // say that the likely type is number. If more is expected the type will be
+ // propagated back.
+ if (agreedType.isNumber()) return HType.NUMBER;
+ return agreedType;
}
bool isLogicalOperator() => logicalOperatorType != IS_NOT_LOGICAL_OPERATOR;
@@ -1842,9 +1926,7 @@ class HPhi extends HInstruction {
class HRelational extends HInvokeBinary {
HRelational(HStatic target, HInstruction left, HInstruction right)
- : super(target, left, right) {
- type = HType.BOOLEAN;
- }
+ : super(target, left, right);
void prepareGvn() {
// Relational expressions can take part in global value numbering
@@ -1858,19 +1940,27 @@ class HRelational extends HInvokeBinary {
}
}
- HType computeDesiredInputType(HInstruction input) {
+ HType computeTypeFromInputTypes() {
+ if (left.isNumber()) return HType.BOOLEAN;
+ return HType.UNKNOWN;
+ }
+
+ HType computeDesiredTypeForInput(HInstruction input) {
// TODO(floitsch): we want the target to be a function.
if (input == target) return HType.UNKNOWN;
// For all relational operations exept HEquals, we expect to only
// get numbers.
- return HType.NUMBER;
+ if (propagatedType.isUnknown() || propagatedType.isBoolean()) {
+ if (left.isTypeUnknown() || left.isNumber()) return HType.NUMBER;
+ }
+ return HType.UNKNOWN;
+ }
+
+ HType get likelyType() {
+ HType.BOOLEAN;
}
bool get builtin() => left.isNumber() && right.isNumber();
- HType computeType() => HType.BOOLEAN;
- // A HRelational goes through the builtin operator or the top level
- // element. Therefore, it always has the expected type.
- bool hasExpectedType() => true;
// TODO(1603): the class should be marked as abstract.
abstract BinaryOperation get operation();
}
@@ -1881,20 +1971,41 @@ class HEquals extends HRelational {
accept(HVisitor visitor) => visitor.visitEquals(this);
bool get builtin() {
- if (left.isNumber() && right.isNumber()) return true;
- if (left is !HConstant) return false;
- HConstant leftConstant = left;
- // TODO(floitsch): we can do better if we know that the constant does not
- // have the equality operator overridden.
- return !leftConstant.constant.isConstructedObject();
+ // All useful types have === semantics.
+ // Note that this includes all constants except the user-constructed
+ // objects.
+ return left.isConstantNull() || left.propagatedType.isUseful();
}
- HType computeType() => HType.BOOLEAN;
+ HType computeTypeFromInputTypes() {
+ if (builtin) {
kasperl 2012/04/16 09:09:43 Single-line return for consistency.
floitsch 2012/04/16 19:59:37 Done.
+ return HType.BOOLEAN;
+ }
+ return HType.UNKNOWN;
+ }
- HType computeDesiredInputType(HInstruction input) {
+ HType computeDesiredTypeForInput(HInstruction input) {
// TODO(floitsch): we want the target to be a function.
if (input == target) return HType.UNKNOWN;
- if (left.isNumber() || right.isNumber()) return HType.NUMBER;
+ if (input == left && right.propagatedType.isUseful()) {
+ // All our useful types have === semantics. But we don't want to
+ // speculatively test for all possible types. Therefore we try to match
+ // the two types. That is, if we see x == 3, then we speculatively test
+ // if x is a number and bailout if it isn't.
+ if (right.isNumber()) return HType.NUMBER; // No need to be more precise.
+ // String equality testing is much more common than array equality
+ // testing.
+ if (right.isStringOrArray()) return HType.STRING;
+ return right.propagatedType;
+ }
+ // String equality testing is much more common than array equality testing.
+ if (input == left && left.isStringOrArray()) {
+ return HType.READABLE_ARRAY;
+ }
+ // String equality testing is much more common than array equality testing.
+ if (input == right && right.isStringOrArray()) {
+ return HType.STRING;
+ }
return HType.UNKNOWN;
}
@@ -1910,10 +2021,10 @@ class HIdentity extends HRelational {
accept(HVisitor visitor) => visitor.visitIdentity(this);
bool get builtin() => true;
- HType computeType() => HType.BOOLEAN;
- bool hasExpectedType() => true;
- HType computeDesiredInputType(HInstruction input) => HType.UNKNOWN;
+ HType get guaranteedType() => HType.BOOLEAN;
+ HType computeTypeFromInputTypes() => HType.BOOLEAN;
+ HType computeDesiredTypeForInput(HInstruction input) => HType.UNKNOWN;
IdentityOperation get operation() => const IdentityOperation();
int typeCode() => 20;
@@ -2013,8 +2124,8 @@ class HLiteralList extends HInstruction {
HLiteralList(inputs) : super(inputs);
toString() => 'literal list';
accept(HVisitor visitor) => visitor.visitLiteralList(this);
- HType computeType() => HType.MUTABLE_ARRAY;
- bool hasExpectedType() => true;
+
+ HType get guaranteedType() => HType.MUTABLE_ARRAY;
void prepareGvn() {
assert(!hasSideEffects());
@@ -2038,16 +2149,22 @@ class HIndex extends HInvokeStatic {
HInstruction get receiver() => inputs[1];
HInstruction get index() => inputs[2];
- HType computeDesiredInputType(HInstruction input) {
+ HType computeDesiredTypeForInput(HInstruction input) {
// TODO(floitsch): we want the target to be a function.
if (input == target) return HType.UNKNOWN;
- if (input == receiver) return HType.STRING_OR_ARRAY;
+ if (input == receiver) {
+ if (index.isTypeUnknown() || index.isNumber()) {
+ return HType.STRING_OR_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 get builtin() => receiver.isStringOrArray();
- HType computeType() => HType.UNKNOWN;
- bool hasExpectedType() => false;
+ bool get builtin() => receiver.isStringOrArray() && index.isInteger();
}
class HIndexAssign extends HInvokeStatic {
@@ -2064,18 +2181,25 @@ class HIndexAssign extends HInvokeStatic {
HInstruction get index() => inputs[2];
HInstruction get value() => inputs[3];
- HType computeDesiredInputType(HInstruction input) {
+ // Note, that we don't have a computeTypeFromInputTypes, since [HIndexAssign]
+ // is never used as input.
+
+ HType computeDesiredTypeForInput(HInstruction input) {
// TODO(floitsch): we want the target to be a function.
if (input == target) return HType.UNKNOWN;
- if (input == receiver) return HType.MUTABLE_ARRAY;
+ if (input == receiver) {
+ if (index.isTypeUnknown() || index.isNumber()) {
+ 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 get builtin() => receiver.isMutableArray();
- HType computeType() => value.type;
- // This instruction does not yield a new value, so it always
- // has the expected type (void).
- bool hasExpectedType() => true;
+ bool get builtin() => receiver.isMutableArray() && index.isInteger();
}
class HIs extends HInstruction {
@@ -2088,8 +2212,7 @@ class HIs extends HInstruction {
HInstruction get expression() => inputs[0];
- HType computeType() => HType.BOOLEAN;
- bool hasExpectedType() => true;
+ HType get guaranteedType() => HType.BOOLEAN;
accept(HVisitor visitor) => visitor.visitIs(this);

Powered by Google App Engine
This is Rietveld 408576698