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

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

Issue 1195573003: dart2js cps: Refactor and optimize string concatenations. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Revert doc comment change 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
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';
+}
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart ('k') | pkg/compiler/lib/src/js_backend/codegen/codegen.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698