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 e5368785336f0b9d4f4bfec139a25e5630dff29f..a06516a76a3003c7b732993184a2798d65ce64e4 100644 |
--- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
+++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
@@ -5,12 +5,11 @@ |
import 'optimizers.dart' show Pass, ParentVisitor; |
import '../constants/constant_system.dart'; |
-import '../constants/expressions.dart'; |
import '../resolution/operators.dart'; |
import '../constants/values.dart'; |
import '../dart_types.dart' as types; |
import '../dart2jslib.dart' as dart2js; |
-import '../tree/tree.dart' show LiteralDartString; |
+import '../tree/tree.dart' show DartString, ConsDartString, LiteralDartString; |
import 'cps_ir_nodes.dart'; |
import '../types/types.dart'; |
import '../types/constants.dart' show computeTypeMask; |
@@ -338,6 +337,26 @@ class ConstantPropagationLattice { |
return null; // TODO(asgerf): Look up type? |
} |
+ AbstractValue stringConstant(String value) { |
+ return constant(new StringConstantValue(new DartString.literal(value))); |
+ } |
+ |
+ AbstractValue stringify(AbstractValue value) { |
+ if (value.isNothing) return nothing; |
+ if (value.isNonConst) return nonConstant(typeSystem.stringType); |
+ ConstantValue constantValue = value.constant; |
+ if (constantValue is StringConstantValue) { |
+ return value; |
+ } else if (constantValue is PrimitiveConstantValue) { |
+ // Note: The primitiveValue for a StringConstantValue is not suitable |
+ // for toString() use since it is a DartString. But the other subclasses |
+ // returns an unwrapped Dart value we can safely convert to a string. |
+ return stringConstant(constantValue.primitiveValue.toString()); |
+ } else { |
+ return nonConstant(typeSystem.stringType); |
+ } |
+ } |
+ |
/// The possible return types of a method that may be targeted by |
/// [typedSelector]. If the given selector is not a [TypedSelector], any |
/// reachable method matching the selector may be targeted. |
@@ -467,9 +486,7 @@ class TransformingVisitor extends RecursiveVisitor { |
/// Make a constant primitive for [constant] and set its entry in [values]. |
Constant makeConstantPrimitive(ConstantValue constant) { |
- ConstantExpression constExp = |
- const ConstantExpressionCreator().convert(constant); |
- Constant primitive = new Constant(constExp, constant); |
+ Constant primitive = new Constant(constant); |
values[primitive] = new AbstractValue.constantValue(constant, |
typeSystem.getTypeOf(constant)); |
return primitive; |
@@ -540,20 +557,20 @@ class TransformingVisitor extends RecursiveVisitor { |
/// True if the given reference is a use that converts its value to a boolean |
/// and only uses the coerced value. |
- bool isBooleanUse(Reference<Primitive> ref) { |
+ bool isBoolifyingUse(Reference<Primitive> ref) { |
Node use = ref.parent; |
return use is IsTrue || |
use is ApplyBuiltinOperator && use.operator == BuiltinOperator.IsFalsy; |
} |
/// True if all uses of [prim] only use its value after boolean conversion. |
- bool isOnlyUsedAsBoolean(Primitive prim) { |
+ bool isAlwaysBoolified(Primitive prim) { |
for (Reference ref = prim.firstRef; ref != null; ref = ref.next) { |
Node use = ref.parent; |
- // Ignore uses in dead primitives. |
+ // Ignore uses in dead identical() nodes. |
// This happens after rewriting identical(x, true) to x. |
- if (use is Primitive && use.hasNoUses) continue; |
- if (!isBooleanUse(ref)) return false; |
+ if (use is Identical && use.hasNoUses) continue; |
+ if (!isBoolifyingUse(ref)) return false; |
} |
return true; |
} |
@@ -595,7 +612,7 @@ class TransformingVisitor extends RecursiveVisitor { |
// Equality is special due to its treatment of null values and the |
// fact that Dart-null corresponds to both JS-null and JS-undefined. |
// Please see documentation for IsFalsy, StrictEq, and LooseEq. |
- bool isBoolified = isOnlyUsedAsBoolean(cont.parameters.single); |
+ bool isBoolified = isAlwaysBoolified(cont.parameters.single); |
// Comparison with null constants. |
if (isBoolified && |
right.isNullConstant && |
@@ -638,6 +655,11 @@ class TransformingVisitor extends RecursiveVisitor { |
return replaceWithBinary(operator, leftArg, rightArg); |
} |
} |
+ else if (lattice.isDefinitelyString(left, allowNull: false) && |
+ lattice.isDefinitelyString(right, allowNull: false)) { |
+ return replaceWithBinary(BuiltinOperator.StringConcatenate, |
+ leftArg, rightArg); |
+ } |
} |
} |
// We should only get here if the node was not specialized. |
@@ -712,12 +734,6 @@ class TransformingVisitor extends RecursiveVisitor { |
super.visitInvokeMethod(node); |
} |
- void visitConcatenateStrings(ConcatenateStrings node) { |
- Continuation cont = node.continuation.definition; |
- if (constifyExpression(node, cont)) return; |
- super.visitConcatenateStrings(node); |
- } |
- |
void visitTypeCast(TypeCast node) { |
Continuation cont = node.continuation.definition; |
@@ -745,6 +761,36 @@ class TransformingVisitor extends RecursiveVisitor { |
super.visitTypeCast(node); |
} |
+ /// Specialize calls to static methods. |
+ /// |
+ /// Returns true if the call was replaced. |
+ bool specializeInvokeStatic(InvokeStatic node) { |
+ // TODO(asgerf): This is written to easily scale to more cases, |
+ // either add more cases or clean up. |
+ Continuation cont = node.continuation.definition; |
+ Primitive arg(int n) => node.arguments[n].definition; |
+ AbstractValue argType(int n) => getValue(arg(n)); |
+ if (node.target.library.isInternalLibrary) { |
+ switch(node.target.name) { |
+ case InternalMethod.Stringify: |
+ if (lattice.isDefinitelyString(argType(0))) { |
+ InvokeContinuation invoke = |
+ new InvokeContinuation(cont, <Primitive>[arg(0)]); |
+ replaceSubtree(node, invoke); |
+ visitInvokeContinuation(invoke); |
+ return true; |
+ } |
+ break; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ void visitInvokeStatic(InvokeStatic node) { |
+ if (constifyExpression(node, node.continuation.definition)) return; |
+ if (specializeInvokeStatic(node)) return; |
+ } |
+ |
AbstractValue getValue(Primitive primitive) { |
AbstractValue value = values[primitive]; |
return value == null ? new AbstractValue.nothing() : value; |
@@ -763,6 +809,64 @@ class TransformingVisitor extends RecursiveVisitor { |
} |
} |
+ void insertLetPrim(Expression node, Primitive prim) { |
+ InteriorNode parent = node.parent; |
+ LetPrim let = new LetPrim(prim); |
+ parent.body = let; |
+ let.body = node; |
+ node.parent = let; |
+ let.parent = parent; |
+ } |
+ |
+ void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
+ DartString getString(AbstractValue value) { |
+ StringConstantValue constant = value.constant; |
+ return constant.primitiveValue; |
+ } |
+ switch (node.operator) { |
+ case BuiltinOperator.StringConcatenate: |
+ // Concatenate consecutive constants. |
+ bool argumentsWereRemoved = false; |
+ int i = 0; |
+ while (i < node.arguments.length - 1) { |
+ int startOfSequence = i; |
+ AbstractValue firstValue = getValue(node.arguments[i++].definition); |
+ if (!firstValue.isConstant) continue; |
+ AbstractValue secondValue = getValue(node.arguments[i++].definition); |
+ if (!secondValue.isConstant) continue; |
+ |
+ DartString string = |
+ new ConsDartString(getString(firstValue), getString(secondValue)); |
+ |
+ // We found a sequence of at least two constants. |
+ // Look for the end of the sequence. |
+ while (i < node.arguments.length) { |
+ AbstractValue value = getValue(node.arguments[i].definition); |
+ if (!value.isConstant) break; |
+ string = new ConsDartString(string, getString(value)); |
+ ++i; |
+ } |
+ Constant prim = |
+ makeConstantPrimitive(new StringConstantValue(string)); |
+ insertLetPrim(node.parent, prim); |
+ for (int k = startOfSequence; k < i; ++k) { |
+ node.arguments[k].unlink(); |
+ node.arguments[k] = null; // Remove the argument after the loop. |
+ } |
+ node.arguments[startOfSequence] = new Reference<Primitive>(prim); |
+ argumentsWereRemoved = true; |
+ } |
+ if (argumentsWereRemoved) { |
+ node.arguments.removeWhere((ref) => ref == null); |
+ } |
+ // TODO(asgerf): Rebalance nested StringConcats that arise from |
+ // rewriting the + operator to StringConcat. |
+ break; |
+ |
+ default: |
+ } |
+ } |
+ |
Primitive visitTypeTest(TypeTest node) { |
Primitive prim = node.value.definition; |
AbstractValue value = getValue(prim); |
@@ -803,6 +907,10 @@ class TransformingVisitor extends RecursiveVisitor { |
} else { |
Primitive newPrim = visit(node.primitive); |
if (newPrim != null) { |
+ if (!values.containsKey(newPrim)) { |
+ // If the type was not set, default to the same type as before. |
+ values[newPrim] = values[node.primitive]; |
+ } |
newPrim.substituteFor(node.primitive); |
RemovalVisitor.remove(node.primitive); |
node.primitive = newPrim; |
@@ -975,11 +1083,30 @@ class TypePropagationVisitor implements Visitor { |
assert(cont.parameters.length == 1); |
Parameter returnValue = cont.parameters[0]; |
- Entity target = node.target; |
- TypeMask returnType = target is FieldElement |
- ? typeSystem.dynamicType |
- : typeSystem.getReturnType(node.target); |
- setValue(returnValue, nonConstant(returnType)); |
+ |
+ /// Sets the value of the target continuation parameter, and possibly |
+ /// try to replace the whole invocation with a constant. |
+ void setResult(AbstractValue updateValue, {bool canReplace: false}) { |
+ setValue(returnValue, updateValue); |
+ if (canReplace && updateValue.isConstant) { |
+ replacements[node] = updateValue.constant; |
+ } else { |
+ // A previous iteration might have tried to replace this. |
+ replacements.remove(node); |
+ } |
+ } |
+ |
+ if (node.target.library.isInternalLibrary) { |
+ switch (node.target.name) { |
+ case InternalMethod.Stringify: |
+ AbstractValue argValue = getValue(node.arguments[0].definition); |
+ setResult(lattice.stringify(argValue), canReplace: true); |
+ return; |
+ } |
+ } |
+ |
+ TypeMask returnType = typeSystem.getReturnType(node.target); |
+ setResult(nonConstant(returnType)); |
} |
void visitInvokeContinuation(InvokeContinuation node) { |
@@ -1052,9 +1179,35 @@ class TypePropagationVisitor implements Visitor { |
} |
void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
- // Not actually reachable yet. |
- // TODO(asgerf): Implement type propagation for builtin operators. |
- setValue(node, nonConstant()); |
+ // Note that most built-in operators do not exist before the transformation |
+ // pass after this analysis has finished. |
+ switch (node.operator) { |
+ case BuiltinOperator.StringConcatenate: |
+ DartString stringValue = const LiteralDartString(''); |
+ for (Reference<Primitive> arg in node.arguments) { |
+ AbstractValue value = getValue(arg.definition); |
+ if (value.isNothing) { |
+ return; // And come back later |
+ } else if (value.isConstant && |
+ value.constant.isString && |
+ stringValue != null) { |
+ StringConstantValue constant = value.constant; |
+ stringValue = |
+ new ConsDartString(stringValue, constant.primitiveValue); |
+ } else { |
+ stringValue = null; |
+ break; |
+ } |
+ } |
+ if (stringValue == null) { |
+ setValue(node, nonConstant(typeSystem.stringType)); |
+ } else { |
+ setValue(node, constantValue(new StringConstantValue(stringValue))); |
+ } |
+ break; |
+ default: |
+ setValue(node, nonConstant()); |
+ } |
} |
void visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
@@ -1076,50 +1229,6 @@ class TypePropagationVisitor implements Visitor { |
setValue(returnValue, nonConstant(typeSystem.getReturnType(node.target))); |
} |
- void visitConcatenateStrings(ConcatenateStrings node) { |
- Continuation cont = node.continuation.definition; |
- setReachable(cont); |
- |
- /// Sets the value of the target continuation parameter, and possibly |
- /// try to replace the whole invocation with a constant. |
- void setResult(AbstractValue 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 |
- // constants, but we could also handle cases such as "foo${42}". |
- bool allStringConstants = node.arguments.every((Reference ref) { |
- if (!(ref.definition is Constant)) { |
- return false; |
- } |
- Constant constant = ref.definition; |
- return constant != null && constant.value.isString; |
- }); |
- |
- TypeMask type = typeSystem.stringType; |
- assert(cont.parameters.length == 1); |
- if (allStringConstants) { |
- // All constant, we can concatenate ourselves. |
- Iterable<String> allStrings = node.arguments.map((Reference ref) { |
- Constant constant = ref.definition; |
- StringConstantValue stringConstant = constant.value; |
- return stringConstant.primitiveValue.slowToString(); |
- }); |
- LiteralDartString dartString = new LiteralDartString(allStrings.join()); |
- ConstantValue constant = new StringConstantValue(dartString); |
- setResult(constantValue(constant, type), canReplace: true); |
- } else { |
- setResult(nonConstant(type)); |
- } |
- } |
- |
void visitThrow(Throw node) { |
} |
@@ -1450,76 +1559,7 @@ class AbstractValue { |
} |
} |
-class ConstantExpressionCreator |
- implements ConstantValueVisitor<ConstantExpression, dynamic> { |
- |
- const ConstantExpressionCreator(); |
- |
- ConstantExpression convert(ConstantValue value) => value.accept(this, null); |
- |
- @override |
- ConstantExpression visitBool(BoolConstantValue constant, _) { |
- return new BoolConstantExpression(constant.primitiveValue); |
- } |
- |
- @override |
- ConstantExpression visitConstructed(ConstructedConstantValue constant, arg) { |
- throw new UnsupportedError("ConstantExpressionCreator.visitConstructed"); |
- } |
- |
- @override |
- ConstantExpression visitDeferred(DeferredConstantValue constant, arg) { |
- throw new UnsupportedError("ConstantExpressionCreator.visitDeferred"); |
- } |
- |
- @override |
- ConstantExpression visitDouble(DoubleConstantValue constant, arg) { |
- return new DoubleConstantExpression(constant.primitiveValue); |
- } |
- |
- @override |
- ConstantExpression visitSynthetic(SyntheticConstantValue constant, arg) { |
- throw new UnsupportedError("ConstantExpressionCreator.visitSynthetic"); |
- } |
- |
- @override |
- ConstantExpression visitFunction(FunctionConstantValue constant, arg) { |
- throw new UnsupportedError("ConstantExpressionCreator.visitFunction"); |
- } |
- |
- @override |
- ConstantExpression visitInt(IntConstantValue constant, arg) { |
- return new IntConstantExpression(constant.primitiveValue); |
- } |
- |
- @override |
- ConstantExpression visitInterceptor(InterceptorConstantValue constant, arg) { |
- throw new UnsupportedError("ConstantExpressionCreator.visitInterceptor"); |
- } |
- |
- @override |
- ConstantExpression visitList(ListConstantValue constant, arg) { |
- throw new UnsupportedError("ConstantExpressionCreator.visitList"); |
- } |
- |
- @override |
- ConstantExpression visitMap(MapConstantValue constant, arg) { |
- throw new UnsupportedError("ConstantExpressionCreator.visitMap"); |
- } |
- |
- @override |
- ConstantExpression visitNull(NullConstantValue constant, arg) { |
- return new NullConstantExpression(); |
- } |
- |
- @override |
- ConstantExpression visitString(StringConstantValue constant, arg) { |
- return new StringConstantExpression( |
- constant.primitiveValue.slowToString()); |
- } |
- |
- @override |
- ConstantExpression visitType(TypeConstantValue constant, arg) { |
- throw new UnsupportedError("ConstantExpressionCreator.visitType"); |
- } |
-} |
+/// Enum-like class with the names of internal methods we care about. |
+abstract class InternalMethod { |
+ static const String Stringify = 'S'; |
+} |