| 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 578589bafdbf0a21863a4c0fd782b5a2af830acf..67159305c2afd2ff430ca50e70d425ddd132dddc 100644
|
| --- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| +++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| @@ -53,6 +53,10 @@ class TypeMaskSystem {
|
| return mask.locateSingleElement(selector, mask, classWorld.compiler);
|
| }
|
|
|
| + bool needsNoSuchMethodHandling(TypeMask mask, Selector selector) {
|
| + return mask.needsNoSuchMethodHandling(selector, classWorld);
|
| + }
|
| +
|
| TypeMask getReceiverType(MethodElement method) {
|
| assert(method.isInstanceMember);
|
| return nonNullSubclass(method.enclosingClass);
|
| @@ -477,21 +481,34 @@ class TransformingVisitor extends RecursiveVisitor {
|
| visit(root);
|
| }
|
|
|
| + /// Sets parent pointers and computes types for the given subtree.
|
| + void reanalyze(Node node) {
|
| + new ParentVisitor().visit(node);
|
| + analyzer.reanalyzeSubtree(node);
|
| + }
|
| +
|
| /// Removes the entire subtree of [node] and inserts [replacement].
|
| - /// All references in the [node] subtree are unlinked, and parent pointers
|
| - /// in [replacement] are initialized and its types recomputed.
|
| + ///
|
| + /// By default, all references in the [node] subtree are unlinked, and parent
|
| + /// pointers in [replacement] are initialized and its types recomputed.
|
| + ///
|
| + /// If the caller needs to manually unlink the node, because some references
|
| + /// were adopted by other nodes, it can be disabled by passing `false`
|
| + /// as the [unlink] parameter.
|
| ///
|
| /// [replacement] must be "fresh", i.e. it must not contain significant parts
|
| /// of the original IR inside of it since the [ParentVisitor] will
|
| /// redundantly reprocess it.
|
| - void replaceSubtree(Expression node, Expression replacement) {
|
| + void replaceSubtree(Expression node, Expression replacement,
|
| + {bool unlink: true}) {
|
| InteriorNode parent = node.parent;
|
| parent.body = replacement;
|
| replacement.parent = parent;
|
| node.parent = null;
|
| - RemovalVisitor.remove(node);
|
| - new ParentVisitor().visit(replacement);
|
| - analyzer.reanalyzeSubtree(replacement);
|
| + if (unlink) {
|
| + RemovalVisitor.remove(node);
|
| + }
|
| + reanalyze(replacement);
|
| }
|
|
|
| /// Make a constant primitive for [constant] and set its entry in [values].
|
| @@ -729,10 +746,144 @@ class TransformingVisitor extends RecursiveVisitor {
|
| }
|
| }
|
|
|
| + /// If [prim] is the parameter to a call continuation, returns the
|
| + /// corresponding call.
|
| + Invoke getInvocationWithResult(Primitive prim) {
|
| + if (prim is Parameter && prim.parent is Continuation) {
|
| + Continuation cont = prim.parent;
|
| + if (cont.hasExactlyOneUse) {
|
| + Node use = cont.firstRef.parent;
|
| + if (use is Invoke) {
|
| + return use;
|
| + }
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + /// True if any side effect immediately before [current] can safely be
|
| + /// postponed until immediately before [target].
|
| + ///
|
| + /// An expression `e` can be moved right before [target] if
|
| + /// `canPostponeSideEffects(e.body, target)` is true and no reference
|
| + /// falls out of scope.
|
| + ///
|
| + /// A more sophisticated analysis would track side-effect dependencies
|
| + /// between `e` and the expressions between `e` and the target.
|
| + bool canPostponeSideEffects(Expression current, Expression target) {
|
| + Expression exp = current;
|
| + while (exp != target) {
|
| + if (exp is LetPrim && exp.primitive.isSafeForReordering) {
|
| + LetPrim let = exp;
|
| + exp = let.body;
|
| + } else if (exp is LetCont) {
|
| + LetCont let = exp;
|
| + exp = let.body;
|
| + } else {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + /// Rewrites an invocation of a torn-off method into a method call directly
|
| + /// on the receiver. For example:
|
| + ///
|
| + /// obj.get$foo().call$<n>(<args>)
|
| + /// =>
|
| + /// obj.foo$<n>(<args>)
|
| + ///
|
| + bool specializeClosureCall(InvokeMethod node) {
|
| + Selector call = node.selector;
|
| + if (!call.isClosureCall) return false;
|
| +
|
| + assert(!isInterceptedSelector(call));
|
| + assert(call.argumentCount == node.arguments.length);
|
| +
|
| + Primitive tearOff = node.receiver.definition;
|
| + // Note: We don't know if [tearOff] is actually a tear-off.
|
| + // We name variables based on the pattern we are trying to match.
|
| +
|
| + if (tearOff is GetStatic && tearOff.element.isFunction) {
|
| + FunctionElement target = tearOff.element;
|
| + FunctionSignature signature = target.functionSignature;
|
| +
|
| + // If the selector does not apply, don't bother (will throw at runtime).
|
| + if (!call.signatureApplies(target)) return false;
|
| +
|
| + // If some optional arguments are missing, give up.
|
| + // TODO(asgerf): Improve optimization by inserting default arguments.
|
| + if (call.argumentCount != signature.parameterCount) return false;
|
| +
|
| + InvokeStatic invoke = new InvokeStatic.byReference(
|
| + target,
|
| + new Selector.fromElement(target),
|
| + node.arguments,
|
| + node.continuation,
|
| + node.sourceInformation);
|
| + node.receiver.unlink();
|
| + replaceSubtree(node, invoke, unlink: false);
|
| + visitInvokeStatic(invoke);
|
| + return true;
|
| + }
|
| + Invoke tearOffInvoke = getInvocationWithResult(tearOff);
|
| + if (tearOffInvoke is InvokeMethod && tearOffInvoke.selector.isGetter) {
|
| + Selector getter = tearOffInvoke.selector;
|
| + Continuation getterCont = tearOffInvoke.continuation.definition;
|
| + Primitive object = tearOffInvoke.receiver.definition;
|
| +
|
| + // Ensure that the object actually has a foo member, since we might
|
| + // otherwise alter a noSuchMethod call.
|
| + TypeMask type = getValue(object).type;
|
| + if (typeSystem.needsNoSuchMethodHandling(type, getter)) return false;
|
| +
|
| + // Determine if the getter invocation can have side-effects.
|
| + Element element = typeSystem.locateSingleElement(type, getter);
|
| + bool isPure = element != null && !element.isGetter;
|
| +
|
| + // 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 the getter call is impure, we risk reordering side effects.
|
| + if (!isPure && !canPostponeSideEffects(getterCont.body, node)) {
|
| + return false;
|
| + }
|
| +
|
| + InvokeMethod invoke = new InvokeMethod.byReference(
|
| + new Reference<Primitive>(object),
|
| + new Selector(SelectorKind.CALL, getter.memberName, call.callStructure),
|
| + type,
|
| + node.arguments,
|
| + node.continuation,
|
| + node.sourceInformation);
|
| + node.receiver.unlink();
|
| + replaceSubtree(node, invoke, unlink: false);
|
| +
|
| + if (tearOff.hasNoUses) {
|
| + // 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.
|
| + getterCont.parameters.clear();
|
| + replaceSubtree(tearOffInvoke, new InvokeContinuation(getterCont, []));
|
| + } else {
|
| + // There are more uses, so we cannot eliminate the getter call. This
|
| + // means we duplicated the effects of the getter call, but we should
|
| + // only get here if the getter has no side effects.
|
| + assert(isPure);
|
| + }
|
| +
|
| + visitInvokeMethod(invoke);
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| void visitInvokeMethod(InvokeMethod node) {
|
| if (constifyExpression(node)) return;
|
| if (specializeOperatorCall(node)) return;
|
| if (specializeFieldAccess(node)) return;
|
| + if (specializeClosureCall(node)) return;
|
|
|
| AbstractValue receiver = getValue(node.receiver.definition);
|
| node.receiverIsNotNull = receiver.isDefinitelyNotNull;
|
| @@ -846,6 +997,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| node.arguments[k] = null; // Remove the argument after the loop.
|
| }
|
| node.arguments[startOfSequence] = new Reference<Primitive>(prim);
|
| + node.arguments[startOfSequence].parent = node;
|
| argumentsWereRemoved = true;
|
| }
|
| if (argumentsWereRemoved) {
|
| @@ -909,16 +1061,15 @@ class TransformingVisitor extends RecursiveVisitor {
|
| newPrim.substituteFor(node.primitive);
|
| RemovalVisitor.remove(node.primitive);
|
| node.primitive = newPrim;
|
| + newPrim.parent = node;
|
| } 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;
|
| + newPrim.parent = node;
|
| + reanalyze(newPrim);
|
| }
|
| if (node.primitive.hasNoUses && node.primitive.isSafeForElimination) {
|
| // Remove unused primitives before entering the body.
|
|
|