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 411cb82b9daaea333804549e7703c40f2af50f15..4e8d40d03a345ae14eb4343b771037e216a70316 100644 |
--- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
+++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
@@ -177,6 +177,12 @@ class TypeMaskSystem { |
return t.nonNullable().satisfies(backend.jsFixedArrayClass, classWorld); |
} |
+ bool isDefinitelyExtendableNativeList(TypeMask t, {bool allowNull: false}) { |
+ if (!allowNull && t.isNullable) return false; |
+ return t.nonNullable().satisfies(backend.jsExtendableArrayClass, |
+ classWorld); |
+ } |
+ |
bool areDisjoint(TypeMask leftType, TypeMask rightType) { |
TypeMask intersection = leftType.intersection(rightType, classWorld); |
return intersection.isEmpty && !intersection.isNullable; |
@@ -331,6 +337,13 @@ class ConstantPropagationLattice { |
allowNull: allowNull); |
} |
+ bool isDefinitelyExtendableNativeList(AbstractValue value, |
+ {bool allowNull: false}) { |
+ return value.isNothing || |
+ typeSystem.isDefinitelyExtendableNativeList(value.type, |
+ allowNull: allowNull); |
+ } |
+ |
/// Returns whether the given [value] is an instance of [type]. |
/// |
/// Since [value] and [type] are not always known, [AbstractBool.Maybe] is |
@@ -1083,6 +1096,8 @@ class TransformingVisitor extends LeafVisitor { |
lattice.isDefinitelyFixedNativeList(listValue, allowNull: true); |
bool isMutable = |
lattice.isDefinitelyMutableNativeList(listValue, allowNull: true); |
+ bool isExtendable = |
+ lattice.isDefinitelyExtendableNativeList(listValue, allowNull: true); |
SourceInformation sourceInfo = node.sourceInformation; |
Continuation cont = node.continuation.definition; |
switch (node.selector.name) { |
@@ -1094,7 +1109,67 @@ class TransformingVisitor extends LeafVisitor { |
push(cps.result); |
return true; |
+ case 'add': |
+ if (!node.selector.isCall || |
+ node.selector.positionalArgumentCount != 1 || |
+ node.selector.namedArgumentCount != 0) { |
+ return false; |
+ } |
+ if (!isExtendable) return false; |
+ Primitive addedItem = getDartArgument(node, 0); |
+ CpsFragment cps = new CpsFragment(sourceInfo); |
+ cps.invokeBuiltin(BuiltinMethod.Push, |
+ list, |
+ <Primitive>[addedItem], |
+ receiverIsNotNull: listValue.isDefinitelyNotNull); |
+ cps.invokeContinuation(cont, [cps.makeNull()]); |
+ replaceSubtree(node, cps.result); |
+ push(cps.result); |
+ return true; |
+ |
+ case 'removeLast': |
+ if (!node.selector.isCall || |
+ node.selector.argumentCount != 0) { |
+ return false; |
+ } |
+ if (!isExtendable) return false; |
+ CpsFragment cps = new CpsFragment(sourceInfo); |
+ Primitive removedItem = cps.invokeBuiltin(BuiltinMethod.Pop, |
+ list, |
+ <Primitive>[], |
+ receiverIsNotNull: listValue.isDefinitelyNotNull); |
+ cps.invokeContinuation(cont, [removedItem]); |
+ replaceSubtree(node, cps.result); |
+ push(cps.result); |
+ return true; |
+ |
+ case 'addAll': |
+ if (!node.selector.isCall || |
+ node.selector.argumentCount != 1) { |
+ return false; |
+ } |
+ if (!isExtendable) return false; |
+ Primitive addedList = getDartArgument(node, 0); |
+ if (addedList is! LiteralList) return false; |
+ LiteralList addedLiteral = addedList; |
+ CpsFragment cps = new CpsFragment(sourceInfo); |
+ cps.invokeBuiltin(BuiltinMethod.Push, |
+ list, |
+ addedLiteral.values.map((ref) => ref.definition).toList(), |
+ receiverIsNotNull: listValue.isDefinitelyNotNull); |
+ cps.invokeContinuation(cont, [cps.makeNull()]); |
+ replaceSubtree(node, cps.result); |
+ push(cps.result); |
+ return true; |
+ |
case '[]': |
+ case 'elementAt': |
+ if (node.selector.name == 'elementAt' && |
+ (!node.selector.isCall || |
+ node.selector.positionalArgumentCount != 1 || |
+ node.selector.namedArgumentCount != 0)) { |
+ return false; |
+ } |
if (listValue.isNullable) return false; |
if (hasTooManyIndexAccesses(list)) return false; |
Primitive index = getDartArgument(node, 0); |
@@ -1326,8 +1401,6 @@ class TransformingVisitor extends LeafVisitor { |
push(invoke); |
return true; |
- // TODO(asgerf): Rewrite 'add', 'removeLast', ... |
- |
default: |
return false; |
} |
@@ -1647,6 +1720,27 @@ class TransformingVisitor extends LeafVisitor { |
} |
} |
+ Primitive visitApplyBuiltinMethod(ApplyBuiltinMethod node) { |
+ if (node.method == BuiltinMethod.Push) { |
+ // Convert consecutive pushes into a single push. |
+ InteriorNode parent = getEffectiveParent(node.parent); |
+ if (parent is LetPrim && parent.primitive is ApplyBuiltinMethod) { |
+ ApplyBuiltinMethod previous = parent.primitive; |
+ if (previous.method == BuiltinMethod.Push && |
+ previous.receiver.definition == node.receiver.definition) { |
+ for (Reference ref in node.arguments) { |
+ previous.arguments.add(ref); |
Kevin Millikin (Google)
2015/08/14 07:56:55
This doesn't look safe. I think it can lift an ar
asgerf
2015/08/14 08:47:17
Oof! You're right.
|
+ ref.parent = previous; |
+ } |
+ node.arguments.clear(); // Avoid unlinking adopted references. |
+ // Replace the old push by a dummy that will get removed. |
+ return makeConstantPrimitive(new NullConstantValue()); |
+ } |
+ } |
+ } |
+ return null; |
+ } |
+ |
Primitive visitTypeTest(TypeTest node) { |
Primitive prim = node.value.definition; |
AbstractValue value = getValue(prim); |
@@ -2059,6 +2153,11 @@ class TypePropagationVisitor implements Visitor { |
} |
} |
+ void visitApplyBuiltinMethod(ApplyBuiltinMethod node) { |
+ // TODO(asgerf): For pop(), use the container type from the TypeMask. |
+ setValue(node, nonConstant()); |
+ } |
+ |
void visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
Continuation cont = node.continuation.definition; |
setReachable(cont); |