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 7ae47a5c134e79a5ad369d2a07259bf3dd9c03d7..c4f557c6953335465dca51320101f809776d0cee 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' show TypeMask, TypesTask; |
import '../types/constants.dart' show computeTypeMask; |
@@ -457,9 +456,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; |
@@ -628,6 +625,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.StringConcat, |
+ leftArg, rightArg); |
+ } |
} |
} |
// We should only get here if the node was not specialized. |
@@ -645,12 +647,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; |
@@ -678,6 +674,24 @@ class TransformingVisitor extends RecursiveVisitor { |
super.visitTypeCast(node); |
} |
+ void visitInvokeStatic(InvokeStatic node) { |
+ Continuation cont = node.continuation.definition; |
+ Primitive arg(int n) => node.arguments[n].definition; |
karlklose
2015/06/19 08:33:45
Could you inline the two helpers?
asgerf
2015/06/19 11:04:34
We're going to need them anyway.
Why I am doing t
|
+ AbstractValue argType(int n) => getValue(arg(n)); |
+ if (node.target.library.isInternalLibrary) { |
karlklose
2015/06/19 08:33:45
Change to one conditional?
if (node.target.libra
asgerf
2015/06/19 11:04:34
See previous reply.
|
+ switch(node.target.name) { |
+ case InternalMethod.Stringify: |
+ if (lattice.isDefinitelyString(argType(0))) { |
+ InvokeContinuation invoke = |
+ new InvokeContinuation(cont, <Primitive>[arg(0)]); |
Kevin Millikin (Google)
2015/06/19 11:36:13
I wonder if we can come up with a convenient and s
asgerf
2015/06/19 12:00:52
Yeah I don't like it either. I tried adding constr
|
+ replaceSubtree(node, invoke); |
+ visitInvokeContinuation(invoke); |
+ } |
+ break; |
+ } |
+ } |
+ } |
+ |
AbstractValue getValue(Primitive primitive) { |
AbstractValue value = values[primitive]; |
return value == null ? new AbstractValue.nothing() : value; |
@@ -696,6 +710,63 @@ 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) { |
+ switch (node.operator) { |
+ case BuiltinOperator.StringConcat: |
karlklose
2015/06/19 08:33:45
Use if instead?
asgerf
2015/06/19 11:04:34
Same as previous reply.
|
+ // 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 getString(AbstractValue value) { |
karlklose
2015/06/19 08:33:45
Consider moving this helper to the top of the meth
asgerf
2015/06/19 11:04:34
Done.
|
+ return (value.constant as StringConstantValue).primitiveValue; |
+ } |
+ 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); |
@@ -732,6 +803,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; |
@@ -904,10 +979,22 @@ 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); |
+ |
+ if (node.target.library.isInternalLibrary) { |
+ switch (node.target.name) { |
karlklose
2015/06/19 08:33:45
Use if?
asgerf
2015/06/19 11:04:34
See previous reply.
|
+ case InternalMethod.Stringify: |
+ AbstractValue argValue = getValue(node.arguments[0].definition); |
+ if (argValue.isNothing) return; // And come back later. |
+ if (lattice.isDefinitelyString(argValue)) { |
+ setValue(returnValue, argValue); |
+ } else { |
+ setValue(returnValue, nonConstant(typeSystem.stringType)); |
+ } |
+ return; |
+ } |
+ } |
+ |
+ TypeMask returnType = typeSystem.getReturnType(node.target); |
setValue(returnValue, nonConstant(returnType)); |
} |
@@ -981,8 +1068,34 @@ class TypePropagationVisitor implements Visitor { |
} |
void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
- // Not actually reachable yet. |
- // TODO(asgerf): Implement type propagation for builtin operators. |
+ // Note that most built-in operators do not exist before the transformation |
+ // pass after this analysis has finished. |
+ switch (node.operator) { |
+ case BuiltinOperator.StringConcat: |
karlklose
2015/06/19 08:33:45
Use if?
asgerf
2015/06/19 11:04:34
Same.
|
+ 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()); |
} |
@@ -1005,50 +1118,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) { |
} |
@@ -1367,76 +1436,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'; |
karlklose
2015/06/19 08:33:45
Make this a predicate to test on the element inste
asgerf
2015/06/19 11:04:34
Are you proposing this?
isStringify(FunctionEleme
|
+} |