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

Unified Diff: pkg/compiler/lib/src/cps_ir/type_propagation.dart

Issue 1153603006: dart2js cps: Type casts and related changes to type propagation. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Another typo in SExpression unstrngifier Created 5 years, 6 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
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart ('k') | pkg/compiler/lib/src/dart_types.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 b9db352a20ab40db6ae8c897b9f680390835d5bb..75a473e891ee3d213d14b37aa177069b73748ae6 100644
--- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
+++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
@@ -25,6 +25,7 @@ abstract class TypeSystem<T> {
T get functionType;
T get boolType;
T get intType;
+ T get numType;
T get stringType;
T get listType;
T get mapType;
@@ -44,6 +45,8 @@ abstract class TypeSystem<T> {
bool isDefinitelyBool(T type);
bool isDefinitelyNotNull(T type);
+
+ AbstractBool isSubtypeOf(T value, types.DartType type, {bool allowNull});
}
class UnitTypeSystem implements TypeSystem<String> {
@@ -53,6 +56,7 @@ class UnitTypeSystem implements TypeSystem<String> {
get dynamicType => UNIT;
get functionType => UNIT;
get intType => UNIT;
+ get numType => UNIT;
get listType => UNIT;
get mapType => UNIT;
get stringType => UNIT;
@@ -67,15 +71,23 @@ class UnitTypeSystem implements TypeSystem<String> {
exact(_) => UNIT;
getTypeOf(_) => UNIT;
+ bool areDisjoint(String leftType, String rightType) {
+ return false;
+ }
+
bool isDefinitelyBool(_) => false;
bool isDefinitelyNotNull(_) => false;
- bool areDisjoint(String leftType, String rightType) {
- return false;
+ AbstractBool isSubtypeOf(value, type, {bool allowNull}) {
+ return AbstractBool.Maybe;
}
}
+enum AbstractBool {
+ True, False, Maybe, Nothing
+}
+
class TypeMaskSystem implements TypeSystem<TypeMask> {
final TypesTask inferrer;
final ClassWorld classWorld;
@@ -85,6 +97,7 @@ class TypeMaskSystem implements TypeSystem<TypeMask> {
TypeMask get functionType => inferrer.functionType;
TypeMask get boolType => inferrer.boolType;
TypeMask get intType => inferrer.intType;
+ TypeMask get numType => inferrer.numType;
TypeMask get stringType => inferrer.stringType;
TypeMask get listType => inferrer.listType;
TypeMask get mapType => inferrer.mapType;
@@ -140,6 +153,149 @@ class TypeMaskSystem implements TypeSystem<TypeMask> {
TypeMask intersection = leftType.intersection(rightType, classWorld);
return intersection.isEmpty && !intersection.isNullable;
}
+
+ AbstractBool isSubtypeOf(TypeMask value,
+ types.DartType type,
+ {bool allowNull}) {
+ assert(allowNull != null);
+ if (type is types.DynamicType) {
+ if (!allowNull && value.isNullable) return AbstractBool.Maybe;
+ return AbstractBool.True;
+ }
+ if (type is types.InterfaceType) {
+ TypeMask typeAsMask = allowNull
+ ? new TypeMask.subtype(type.element, classWorld)
+ : new TypeMask.nonNullSubtype(type.element, classWorld);
+ if (areDisjoint(value, typeAsMask)) {
+ // Disprove the subtype relation based on the class alone.
+ return AbstractBool.False;
+ }
+ if (!type.treatAsRaw) {
+ // If there are type arguments, we cannot prove the subtype relation,
+ // because the type arguments are unknown on both the value and type.
+ return AbstractBool.Maybe;
+ }
+ if (typeAsMask.containsMask(value, classWorld)) {
+ // All possible values are contained in the set of allowed values.
+ // Note that we exploit the fact that [typeAsMask] is an exact
+ // representation of [type], not an approximation.
+ return AbstractBool.True;
+ }
+ // The value is neither contained in the type, nor disjoint from the type.
+ return AbstractBool.Maybe;
+ }
+ // TODO(asgerf): Support function types, and what else might be missing.
+ return AbstractBool.Maybe;
+ }
+}
+
+class ConstantPropagationLattice<T> {
+ final TypeSystem<T> typeSystem;
+ final ConstantSystem constantSystem;
+ final types.DartTypes dartTypes;
+
+ ConstantPropagationLattice(this.typeSystem,
+ this.constantSystem,
+ this.dartTypes);
+
+ final _AbstractValue<T> nothing = new _AbstractValue<T>.nothing();
+
+ _AbstractValue<T> constant(ConstantValue value) {
+ return new _AbstractValue<T>.constantValue(value,
+ typeSystem.getTypeOf(value));
+ }
+
+ _AbstractValue<T> nonConstant(T type) {
+ return new _AbstractValue<T>.nonConstant(type);
+ }
+
+ _AbstractValue<T> get anything {
+ return new _AbstractValue<T>.nonConstant(typeSystem.dynamicType);
+ }
+
+ /// Returns whether the given [value] is an instance of [type].
+ ///
+ /// Since [value] and [type] are not always known, [AbstractBool.Maybe] is
+ /// returned if the answer is not known.
+ ///
+ /// [AbstractBool.Nothing] is returned if [value] is nothing.
+ ///
+ /// If [allowNull] is true, `null` is considered to an instance of anything,
+ /// otherwise it is only considered an instance of [Object], [dynamic], and
+ /// [Null].
+ AbstractBool isSubtypeOf(_AbstractValue<T> value,
+ types.DartType type,
+ {bool allowNull}) {
+ assert(allowNull != null);
+ if (value.isNothing) {
+ return AbstractBool.Nothing;
+ }
+ if (value.isConstant) {
+ if (value.constant.isNull) {
+ if (allowNull ||
+ type.isObject ||
+ type.isDynamic ||
+ type == dartTypes.coreTypes.nullType) {
+ return AbstractBool.True;
+ }
+ if (type is types.TypeVariableType) {
+ return AbstractBool.Maybe;
+ }
+ return AbstractBool.False;
+ }
+ types.DartType valueType = value.constant.getType(dartTypes.coreTypes);
+ if (constantSystem.isSubtype(dartTypes, valueType, type)) {
+ return AbstractBool.True;
+ }
+ if (!dartTypes.isPotentialSubtype(valueType, type)) {
+ return AbstractBool.False;
+ }
+ return AbstractBool.Maybe;
+ }
+ return typeSystem.isSubtypeOf(value.type, type, allowNull: allowNull);
+ }
+
+ /// Returns the possible results of applying [operator] to [value],
+ /// assuming the operation does not throw.
+ ///
+ /// Because we do not explicitly track thrown values, we currently use the
+ /// convention that constant values are returned from this method only
+ /// if the operation is known not to throw.
+ _AbstractValue<T> unaryOp(UnaryOperator operator,
+ _AbstractValue<T> value) {
+ // TODO(asgerf): Also return information about whether this can throw?
+ if (value.isNothing) {
+ return nothing;
+ }
+ if (value.isConstant) {
+ UnaryOperation operation = constantSystem.lookupUnary(operator);
+ ConstantValue result = operation.fold(value.constant);
+ if (result == null) return anything;
+ return constant(result);
+ }
+ return anything; // TODO(asgerf): Look up type.
+ }
+
+ /// Returns the possible results of applying [operator] to [left], [right],
+ /// assuming the operation does not throw.
+ ///
+ /// Because we do not explicitly track thrown values, we currently use the
+ /// convention that constant values are returned from this method only
+ /// if the operation is known not to throw.
+ _AbstractValue<T> binaryOp(BinaryOperator operator,
+ _AbstractValue<T> left,
+ _AbstractValue<T> right) {
+ 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);
+ }
+ return anything; // TODO(asgerf): Look up type.
+ }
}
/**
@@ -162,35 +318,41 @@ class TypePropagator<T> extends Pass {
final ConstantSystem _constantSystem;
final TypeSystem _typeSystem;
final dart2js.InternalErrorFunction _internalError;
- final Map<Node, _AbstractValue> _types;
+ final Map<Primitive, _AbstractValue> _types = <Primitive, _AbstractValue>{};
TypePropagator(this._dartTypes,
this._constantSystem,
this._typeSystem,
- this._internalError)
- : _types = <Node, _AbstractValue>{};
+ this._internalError);
@override
void rewrite(FunctionDefinition root) {
// Set all parent pointers.
new ParentVisitor().visit(root);
+ ConstantPropagationLattice<T> lattice = new ConstantPropagationLattice<T>(
+ _typeSystem, _constantSystem, _dartTypes);
+ Map<Expression, ConstantValue> replacements = <Expression, ConstantValue>{};
+
// Analyze. In this phase, the entire term is analyzed for reachability
// and the abstract value of each expression.
_TypePropagationVisitor<T> analyzer = new _TypePropagationVisitor<T>(
- _constantSystem,
- _typeSystem,
+ lattice,
_types,
- _internalError,
- _dartTypes);
+ replacements,
+ _internalError);
analyzer.analyze(root);
// Transform. Uses the data acquired in the previous analysis phase to
// replace branches with fixed targets and side-effect-free expressions
- // with constant results.
+ // with constant results or existing values that are in scope.
_TransformingVisitor<T> transformer = new _TransformingVisitor<T>(
- analyzer.reachableNodes, analyzer.values, _internalError, _typeSystem);
+ lattice,
+ analyzer.reachableNodes,
+ analyzer.values,
+ replacements,
+ _internalError);
transformer.transform(root);
}
@@ -204,49 +366,52 @@ class TypePropagator<T> extends Pass {
class _TransformingVisitor<T> extends RecursiveVisitor {
final Set<Node> reachable;
final Map<Node, _AbstractValue> values;
- final TypeSystem<T> typeSystem;
+ final Map<Expression, ConstantValue> replacements;
+ final ConstantPropagationLattice<T> valueLattice;
+
+ TypeSystem<T> get typeSystem => valueLattice.typeSystem;
final dart2js.InternalErrorFunction internalError;
- _TransformingVisitor(this.reachable,
+ _TransformingVisitor(this.valueLattice,
+ this.reachable,
this.values,
- this.internalError,
- this.typeSystem);
+ this.replacements,
+ this.internalError);
void transform(FunctionDefinition root) {
visit(root);
}
+ Constant makeConstantPrimitive(ConstantValue constant) {
+ ConstantExpression constExp =
+ const ConstantExpressionCreator().convert(constant);
+ Constant primitive = new Constant(constExp, constant);
+ values[primitive] = new _AbstractValue.constantValue(constant,
+ typeSystem.getTypeOf(constant));
+ return primitive;
+ }
+
/// Given an expression with a known constant result and a continuation,
/// replaces the expression by a new LetPrim / InvokeContinuation construct.
/// `unlink` is a closure responsible for unlinking all removed references.
LetPrim constifyExpression(Expression node,
Continuation continuation,
void unlink()) {
- _AbstractValue value = values[node];
- if (value == null || !value.isConstant) {
- return null;
- }
+ ConstantValue constant = replacements[node];
+ if (constant == null) return null;
assert(continuation.parameters.length == 1);
+ InteriorNode parent = node.parent;
+ Constant primitive = makeConstantPrimitive(constant);
+ LetPrim letPrim = new LetPrim(primitive);
- // Set up the replacement structure.
- PrimitiveConstantValue primitiveConstant = value.constant;
- ConstantExpression constExp =
- const ConstantExpressionCreator().convert(primitiveConstant);
- Constant constant = new Constant(constExp, primitiveConstant);
- LetPrim letPrim = new LetPrim(constant);
InvokeContinuation invoke =
- new InvokeContinuation(continuation, <Primitive>[constant]);
-
- invoke.parent = constant.parent = letPrim;
+ new InvokeContinuation(continuation, <Primitive>[primitive]);
+ parent.body = letPrim;
letPrim.body = invoke;
-
- // Replace the method invocation.
-
- InteriorNode parent = node.parent;
+ invoke.parent = letPrim;
letPrim.parent = parent;
- parent.body = letPrim;
unlink();
@@ -334,20 +499,34 @@ class _TransformingVisitor<T> extends RecursiveVisitor {
}
}
- // See [visitInvokeMethod].
- void visitTypeOperator(TypeOperator node) {
+ void visitTypeCast(TypeCast node) {
Continuation cont = node.continuation.definition;
- LetPrim letPrim = constifyExpression(node, cont, () {
- node.value.unlink();
- node.typeArguments.forEach((Reference ref) => ref.unlink());
- node.continuation.unlink();
- });
+ InteriorNode parent = node.parent;
- if (letPrim == null) {
- super.visitTypeOperator(node);
- } else {
- visitLetPrim(letPrim);
+ _AbstractValue<T> value = getValue(node.value.definition);
+ switch (valueLattice.isSubtypeOf(value, node.type, allowNull: true)) {
+ case AbstractBool.Maybe:
+ case AbstractBool.Nothing:
+ break;
+
+ case AbstractBool.True:
+ // Cast always succeeds, replace it with InvokeContinuation.
+ InvokeContinuation invoke =
+ new InvokeContinuation.fromCall(node.continuation, node.value);
+ parent.body = invoke;
+ invoke.parent = parent;
+ super.visitInvokeContinuation(invoke);
+ return;
+
+ case AbstractBool.False:
+ // Cast always fails, remove unreachable continuation body.
+ assert(!reachable.contains(cont));
+ RemovalVisitor.remove(cont.body);
+ cont.body = new Unreachable()..parent = cont;
+ break;
}
+
+ super.visitTypeCast(node);
}
_AbstractValue<T> getValue(Primitive primitive) {
@@ -367,6 +546,24 @@ class _TransformingVisitor<T> extends RecursiveVisitor {
left.substituteFor(node);
}
}
+
+ void visitLetPrim(LetPrim node) {
+ _AbstractValue<T> value = getValue(node.primitive);
+ if (node.primitive is! Constant && value.isConstant) {
+ // If the value is a known constant, compile it as a constant.
+ Constant newPrim = makeConstantPrimitive(value.constant);
+ LetPrim newLet = new LetPrim(newPrim);
+ node.parent.body = newLet;
+ newLet.body = node.body;
+ node.body.parent = newLet;
+ newLet.parent = node.parent;
+ newPrim.substituteFor(node.primitive);
+ RemovalVisitor.remove(node.primitive);
+ visit(newLet.body);
+ } else {
+ super.visitLetPrim(node);
+ }
+ }
}
/**
@@ -391,10 +588,10 @@ class _TypePropagationVisitor<T> implements Visitor {
// since their lattice value has changed.
final Set<Definition> defWorkset = new Set<Definition>();
- final ConstantSystem constantSystem;
- final TypeSystem<T> typeSystem;
+ final ConstantPropagationLattice<T> valueLattice;
final dart2js.InternalErrorFunction internalError;
- final types.DartTypes _dartTypes;
+
+ TypeSystem get typeSystem => valueLattice.typeSystem;
_AbstractValue<T> nothing = new _AbstractValue.nothing();
@@ -412,14 +609,16 @@ class _TypePropagationVisitor<T> implements Visitor {
// Stores the current lattice value for nodes. Note that it contains not only
// definitions as keys, but also expressions such as method invokes.
// Access through [getValue] and [setValue].
- final Map<Node, _AbstractValue<T>> values;
+ final Map<Primitive, _AbstractValue<T>> values;
+
+ /// Expressions that invoke their call continuation with a constant value
+ /// and without any side effects. These can be replaced by the constant.
+ final Map<Expression, ConstantValue> replacements;
- _TypePropagationVisitor(this.constantSystem,
- TypeSystem typeSystem,
+ _TypePropagationVisitor(this.valueLattice,
this.values,
- this.internalError,
- this._dartTypes)
- : this.typeSystem = typeSystem;
+ this.replacements,
+ this.internalError);
void analyze(FunctionDefinition root) {
reachableNodes.clear();
@@ -560,63 +759,54 @@ class _TypePropagationVisitor<T> implements Visitor {
Continuation cont = node.continuation.definition;
setReachable(cont);
- /// Sets the value of both the current node and the target continuation
- /// parameter.
- void setValues(_AbstractValue<T> updateValue) {
- setValue(node, updateValue);
+ /// Sets the value of the target continuation parameter, and possibly
+ /// try to replace the whole invocation with a constant.
+ void setResult(_AbstractValue<T> updateValue, {bool canReplace: false}) {
Parameter returnValue = cont.parameters[0];
setValue(returnValue, updateValue);
+ if (canReplace && updateValue.isConstant) {
+ replacements[node] = updateValue.constant;
+ } else {
+ // A previous iteration might have tried to replace this.
+ replacements.remove(node);
+ }
}
_AbstractValue<T> lhs = getValue(node.receiver.definition);
if (lhs.isNothing) {
return; // And come back later.
- } else if (lhs.isNonConst) {
- setValues(nonConstant(typeSystem.getSelectorReturnType(node.selector)));
- return;
- } else if (!node.selector.isOperator) {
+ }
+ if (!node.selector.isOperator) {
// TODO(jgruber): Handle known methods on constants such as String.length.
- setValues(nonConstant());
+ setResult(nonConstant(typeSystem.getSelectorReturnType(node.selector)));
return;
}
+ // TODO(asgerf): Support constant folding on intercepted calls!
+
// Calculate the resulting constant if possible.
- ConstantValue result;
+ _AbstractValue<T> result;
String opname = node.selector.name;
if (node.selector.argumentCount == 0) {
// Unary operator.
-
if (opname == "unary-") {
opname = "-";
}
- UnaryOperation operation = constantSystem.lookupUnary(
- UnaryOperator.parse(opname));
- if (operation != null) {
- result = operation.fold(lhs.constant);
- }
+ UnaryOperator operator = UnaryOperator.parse(opname);
+ result = valueLattice.unaryOp(operator, lhs);
} else if (node.selector.argumentCount == 1) {
// Binary operator.
-
_AbstractValue<T> rhs = getValue(node.arguments[0].definition);
- if (!rhs.isConstant) {
- setValues(nonConstant());
- return;
- }
-
- BinaryOperation operation = constantSystem.lookupBinary(
- BinaryOperator.parse(opname));
- if (operation != null) {
- result = operation.fold(lhs.constant, rhs.constant);
- }
+ BinaryOperator operator = BinaryOperator.parse(opname);
+ result = valueLattice.binaryOp(operator, lhs, rhs);
}
// Update value of the continuation parameter. Again, this is effectively
// a phi.
if (result == null) {
- setValues(nonConstant());
+ setResult(nonConstant());
} else {
- T type = typeSystem.getTypeOf(result);
- setValues(constantValue(result, type));
+ setResult(result, canReplace: true);
}
}
@@ -643,10 +833,17 @@ class _TypePropagationVisitor<T> implements Visitor {
Continuation cont = node.continuation.definition;
setReachable(cont);
- void setValues(_AbstractValue<T> updateValue) {
- setValue(node, updateValue);
+ /// Sets the value of the target continuation parameter, and possibly
+ /// try to replace the whole invocation with a constant.
+ void setResult(_AbstractValue<T> updateValue, {bool canReplace: false}) {
Parameter returnValue = cont.parameters[0];
setValue(returnValue, updateValue);
+ if (canReplace && updateValue.isConstant) {
+ replacements[node] = updateValue.constant;
+ } else {
+ // A previous iteration might have tried to replace this.
+ replacements.remove(node);
+ }
}
// TODO(jgruber): Currently we only optimize if all arguments are string
@@ -670,9 +867,9 @@ class _TypePropagationVisitor<T> implements Visitor {
});
LiteralDartString dartString = new LiteralDartString(allStrings.join());
ConstantValue constant = new StringConstantValue(dartString);
- setValues(constantValue(constant, type));
+ setResult(constantValue(constant, type), canReplace: true);
} else {
- setValues(nonConstant(type));
+ setResult(nonConstant(type));
}
}
@@ -682,6 +879,9 @@ class _TypePropagationVisitor<T> implements Visitor {
void visitRethrow(Rethrow node) {
}
+ void visitUnreachable(Unreachable node) {
+ }
+
void visitNonTailThrow(NonTailThrow node) {
internalError(null, 'found non-tail throw after they were eliminated');
}
@@ -709,50 +909,47 @@ class _TypePropagationVisitor<T> implements Visitor {
}
}
- void visitTypeOperator(TypeOperator node) {
- Continuation cont = node.continuation.definition;
- setReachable(cont);
+ void visitTypeTest(TypeTest node) {
+ _AbstractValue<T> input = getValue(node.value.definition);
+ T boolType = typeSystem.boolType;
+ switch(valueLattice.isSubtypeOf(input, node.type, allowNull: false)) {
+ case AbstractBool.Nothing:
+ break; // And come back later.
- void setValues(_AbstractValue<T> updateValue) {
- setValue(node, updateValue);
- Parameter returnValue = cont.parameters[0];
- setValue(returnValue, updateValue);
- }
+ case AbstractBool.True:
+ setValue(node, constantValue(new TrueConstantValue(), boolType));
+ break;
- if (node.isTypeCast) {
- // TODO(jgruber): Add support for `as` casts.
- setValues(nonConstant());
- return;
+ case AbstractBool.False:
+ setValue(node, constantValue(new FalseConstantValue(), boolType));
+ break;
+
+ case AbstractBool.Maybe:
+ setValue(node, nonConstant(boolType));
+ break;
}
+ }
- _AbstractValue<T> cell = getValue(node.value.definition);
- if (cell.isNothing) {
- return; // And come back later.
- } else if (cell.isConstant && node.type.kind == types.TypeKind.INTERFACE) {
- // Receiver is a constant, perform is-checks at compile-time.
-
- types.InterfaceType checkedType = node.type;
- ConstantValue constant = cell.constant;
- types.DartType constantType = constant.getType(_dartTypes.coreTypes);
-
- T type = typeSystem.boolType;
- _AbstractValue<T> result;
- if (constant.isNull &&
- checkedType != _dartTypes.coreTypes.nullType &&
- checkedType != _dartTypes.coreTypes.objectType) {
- // `(null is Type)` is true iff Type is in { Null, Object }.
- result = constantValue(new FalseConstantValue(), type);
- } else {
- // Otherwise, perform a standard subtype check.
- result = constantValue(
- constantSystem.isSubtype(_dartTypes, constantType, checkedType)
- ? new TrueConstantValue()
- : new FalseConstantValue(),
- type);
- }
- setValues(result);
- } else {
- setValues(nonConstant(typeSystem.boolType));
+ void visitTypeCast(TypeCast node) {
+ Continuation cont = node.continuation.definition;
+ _AbstractValue<T> input = getValue(node.value.definition);
+ switch (valueLattice.isSubtypeOf(input, node.type, allowNull: true)) {
+ case AbstractBool.Nothing:
+ break; // And come back later.
+
+ case AbstractBool.True:
+ setReachable(cont);
+ setValue(cont.parameters.single, input);
+ break;
+
+ case AbstractBool.False:
+ break; // Cast fails. Continuation should remain unreachable.
+
+ case AbstractBool.Maybe:
+ // TODO(asgerf): Narrow type of output to those that survive the cast.
+ setReachable(cont);
+ setValue(cont.parameters.single, input);
+ break;
}
}
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart ('k') | pkg/compiler/lib/src/dart_types.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698