| 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 3212f9406308c66fd34bf3c33220d4125f53e861..1ac163ee538b0732f31819e3dfb1f7c485a61acb 100644
|
| --- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| +++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| @@ -1,6 +1,7 @@
|
| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
| // for details. All rights reserved. Use of this source code is governed by a
|
| // BSD-style license that can be found in the LICENSE file.
|
| +library dart2js.cps_ir.type_propagation;
|
|
|
| import 'optimizers.dart';
|
|
|
| @@ -700,6 +701,7 @@ class TransformingVisitor extends LeafVisitor {
|
| void visitLetPrim(LetPrim node) {
|
| AbstractValue value = getValue(node.primitive);
|
| if (node.primitive is! Constant &&
|
| + node.primitive is! Refinement &&
|
| node.primitive.isSafeForElimination &&
|
| value.isConstant) {
|
| // If the value is a known constant, compile it as a constant.
|
| @@ -1132,16 +1134,17 @@ class TransformingVisitor extends LeafVisitor {
|
| /// This is a short-term solution to avoid inserting a lot of bounds checks,
|
| /// since there is currently no optimization for eliminating them.
|
| bool hasTooManyIndexAccesses(Primitive receiver) {
|
| + receiver = receiver.effectiveDefinition;
|
| int count = 0;
|
| - for (Reference ref = receiver.firstRef; ref != null; ref = ref.next) {
|
| + for (Reference ref in receiver.effectiveUses) {
|
| Node use = ref.parent;
|
| if (use is InvokeMethod &&
|
| (use.selector.isIndex || use.selector.isIndexSet) &&
|
| - getDartReceiver(use) == receiver) {
|
| + getDartReceiver(use).sameValue(receiver)) {
|
| ++count;
|
| - } else if (use is GetIndex && use.object.definition == receiver) {
|
| + } else if (use is GetIndex && use.object.definition.sameValue(receiver)) {
|
| ++count;
|
| - } else if (use is SetIndex && use.object.definition == receiver) {
|
| + } else if (use is SetIndex && use.object.definition.sameValue(receiver)) {
|
| ++count;
|
| }
|
| if (count > 2) return true;
|
| @@ -1322,7 +1325,7 @@ class TransformingVisitor extends LeafVisitor {
|
| // Check that all uses of the iterator are 'moveNext' and 'current'.
|
| assert(!isInterceptedSelector(Selectors.moveNext));
|
| assert(!isInterceptedSelector(Selectors.current));
|
| - for (Reference ref = iterator.firstRef; ref != null; ref = ref.next) {
|
| + for (Reference ref in iterator.effectiveUses) {
|
| if (ref.parent is! InvokeMethod) return false;
|
| InvokeMethod use = ref.parent;
|
| if (ref != use.receiver) return false;
|
| @@ -1339,8 +1342,8 @@ class TransformingVisitor extends LeafVisitor {
|
| MutableVariable current = new MutableVariable(new LoopItemEntity());
|
|
|
| // Rewrite all uses of the iterator.
|
| - while (iterator.firstRef != null) {
|
| - InvokeMethod use = iterator.firstRef.parent;
|
| + for (Reference ref in iterator.effectiveUses) {
|
| + InvokeMethod use = ref.parent;
|
| Continuation useCont = use.continuation.definition;
|
| if (use.selector == Selectors.current) {
|
| // Rewrite iterator.current to a use of the 'current' variable.
|
| @@ -1446,6 +1449,9 @@ class TransformingVisitor extends LeafVisitor {
|
| }
|
| }
|
|
|
| + // All effective uses have been rewritten.
|
| + destroyRefinementsOfDeadPrimitive(iterator);
|
| +
|
| // Rewrite the iterator call to initializers for 'index' and 'current'.
|
| CpsFragment cps = new CpsFragment();
|
| cps.letMutable(index, cps.makeZero());
|
| @@ -1488,7 +1494,8 @@ class TransformingVisitor extends LeafVisitor {
|
| while (true) {
|
| Node parent = node.parent;
|
| if (parent is LetCont ||
|
| - parent is LetPrim && parent.primitive.isSafeForReordering) {
|
| + parent is LetPrim && parent.primitive.isSafeForReordering ||
|
| + parent is LetPrim && parent.primitive is Refinement) {
|
| node = parent;
|
| } else {
|
| return parent;
|
| @@ -1510,7 +1517,7 @@ class TransformingVisitor extends LeafVisitor {
|
| assert(!isInterceptedSelector(call));
|
| assert(call.argumentCount == node.arguments.length);
|
|
|
| - Primitive tearOff = node.receiver.definition;
|
| + Primitive tearOff = node.receiver.definition.effectiveDefinition;
|
| // Note: We don't know if [tearOff] is actually a tear-off.
|
| // We name variables based on the pattern we are trying to match.
|
|
|
| @@ -1545,9 +1552,6 @@ class TransformingVisitor extends LeafVisitor {
|
|
|
| Continuation getterCont = tearOffInvoke.continuation.definition;
|
|
|
| - // TODO(asgerf): Support torn-off intercepted methods.
|
| - if (isInterceptedSelector(getter)) return false;
|
| -
|
| Primitive object = tearOffInvoke.receiver.definition;
|
|
|
| // Ensure that the object actually has a foo member, since we might
|
| @@ -1561,7 +1565,7 @@ class TransformingVisitor extends LeafVisitor {
|
|
|
| // If there are multiple uses, we cannot eliminate the getter call and
|
| // therefore risk duplicating its side effects.
|
| - if (!isPure && tearOff.hasMultipleUses) return false;
|
| + if (!isPure && tearOff.hasMultipleEffectiveUses) return false;
|
|
|
| // If the getter call is impure, we risk reordering side effects.
|
| if (!isPure && getEffectiveParent(node) != getterCont) {
|
| @@ -1578,10 +1582,11 @@ class TransformingVisitor extends LeafVisitor {
|
| node.receiver.unlink();
|
| replaceSubtree(node, invoke, unlink: false);
|
|
|
| - if (tearOff.hasNoUses) {
|
| + if (tearOff.hasNoEffectiveUses) {
|
| // Eliminate the getter call if it has no more uses.
|
| // This cannot be delegated to other optimizations because we need to
|
| // avoid duplication of side effects.
|
| + destroyRefinementsOfDeadPrimitive(tearOff);
|
| getterCont.parameters.clear();
|
| replaceSubtree(tearOffInvoke, new InvokeContinuation(getterCont, []));
|
| } else {
|
| @@ -1597,6 +1602,18 @@ class TransformingVisitor extends LeafVisitor {
|
| return false;
|
| }
|
|
|
| + void destroyRefinementsOfDeadPrimitive(Primitive prim) {
|
| + while (prim.firstRef != null) {
|
| + Refinement refine = prim.firstRef.parent;
|
| + destroyRefinementsOfDeadPrimitive(refine);
|
| + LetPrim letPrim = refine.parent;
|
| + InteriorNode parent = letPrim.parent;
|
| + parent.body = letPrim.body;
|
| + letPrim.body.parent = parent;
|
| + prim.firstRef.unlink();
|
| + }
|
| + }
|
| +
|
| /// Inlines a single-use closure if it leaves the closure object with only
|
| /// field accesses. This is optimized later by [ScalarReplacer].
|
| bool specializeSingleUseClosureCall(InvokeMethod node) {
|
| @@ -1692,7 +1709,7 @@ class TransformingVisitor extends LeafVisitor {
|
| node.receiverIsNotNull = receiver.isDefinitelyNotNull;
|
|
|
| if (isInterceptedSelector(node.selector) &&
|
| - node.receiver.definition == node.arguments[0].definition) {
|
| + node.receiver.definition.sameValue(node.arguments[0].definition)) {
|
| // The receiver and first argument are the same; that means we already
|
| // determined in visitInterceptor that we are targeting a non-interceptor.
|
|
|
| @@ -1986,7 +2003,7 @@ class TransformingVisitor extends LeafVisitor {
|
| if (parent is LetPrim && parent.primitive is ApplyBuiltinMethod) {
|
| ApplyBuiltinMethod previous = parent.primitive;
|
| if (previous.method == BuiltinMethod.Push &&
|
| - previous.receiver.definition == node.receiver.definition) {
|
| + previous.receiver.definition.sameValue(node.receiver.definition)) {
|
| // We found two consecutive pushes.
|
| // Move all arguments from the first push onto the second one.
|
| List<Reference<Primitive>> arguments = previous.arguments;
|
| @@ -2120,6 +2137,8 @@ class TypePropagationVisitor implements Visitor {
|
|
|
| JavaScriptBackend get backend => typeSystem.backend;
|
|
|
| + World get classWorld => typeSystem.classWorld;
|
| +
|
| AbstractValue get nothing => lattice.nothing;
|
|
|
| AbstractValue nonConstant([TypeMask type]) => lattice.nonConstant(type);
|
| @@ -2560,9 +2579,15 @@ class TypePropagationVisitor implements Visitor {
|
| 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);
|
| + TypeMask type = input.type;
|
| + if (node.type.element is ClassElement) {
|
| + // Narrow type of output to those that survive the cast.
|
| + type = type.intersection(
|
| + new TypeMask.subtype(node.type.element, classWorld),
|
| + classWorld);
|
| + }
|
| + setValue(cont.parameters.single, nonConstant(type));
|
| break;
|
| }
|
| }
|
| @@ -2676,7 +2701,7 @@ class TypePropagationVisitor implements Visitor {
|
| }
|
|
|
| @override
|
| - visitTypeExpression(TypeExpression node) {
|
| + void visitTypeExpression(TypeExpression node) {
|
| // TODO(karlklose): come up with a type marker for JS entities or switch to
|
| // real constants of type [Type].
|
| setValue(node, nonConstant());
|
| @@ -2688,7 +2713,7 @@ class TypePropagationVisitor implements Visitor {
|
| }
|
|
|
| @override
|
| - visitForeignCode(ForeignCode node) {
|
| + void visitForeignCode(ForeignCode node) {
|
| if (node.continuation != null) {
|
| Continuation continuation = node.continuation.definition;
|
| setReachable(continuation);
|
| @@ -2715,10 +2740,23 @@ class TypePropagationVisitor implements Visitor {
|
| }
|
|
|
| @override
|
| - visitAwait(Await node) {
|
| + void visitAwait(Await node) {
|
| Continuation continuation = node.continuation.definition;
|
| setReachable(continuation);
|
| }
|
| +
|
| + @override
|
| + void visitRefinement(Refinement node) {
|
| + AbstractValue value = getValue(node.value.definition);
|
| + if (value.isNothing || typeSystem.areDisjoint(value.type, node.type)) {
|
| + setValue(node, nothing);
|
| + } else if (value.isConstant) {
|
| + setValue(node, value);
|
| + } else {
|
| + setValue(node,
|
| + nonConstant(value.type.intersection(node.type, classWorld)));
|
| + }
|
| + }
|
| }
|
|
|
| /// Represents the abstract value of a primitive value at some point in the
|
|
|