| 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 25ee9add7af75f7546a528a9913e2fb9eafdc63b..839e666b56f68b23f078efcf1b942caa4ad99e04 100644
|
| --- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| +++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
|
| @@ -546,7 +546,7 @@ final Map<String, BuiltinOperator> NumBinaryBuiltins =
|
| * Uses the information from a preceding analysis pass in order to perform the
|
| * actual transformations on the CPS graph.
|
| */
|
| -class TransformingVisitor extends RecursiveVisitor {
|
| +class TransformingVisitor extends LeafVisitor {
|
| final TypePropagationVisitor analyzer;
|
| final Map<Expression, ConstantValue> replacements;
|
| final ConstantPropagationLattice lattice;
|
| @@ -560,6 +560,8 @@ class TransformingVisitor extends RecursiveVisitor {
|
|
|
| final dart2js.InternalErrorFunction internalError;
|
|
|
| + final List<Node> stack = <Node>[];
|
| +
|
| TransformingVisitor(this.compiler,
|
| this.lattice,
|
| this.analyzer,
|
| @@ -567,9 +569,81 @@ class TransformingVisitor extends RecursiveVisitor {
|
| this.internalError);
|
|
|
| void transform(FunctionDefinition root) {
|
| - visit(root);
|
| + push(root.body);
|
| + while (stack.isNotEmpty) {
|
| + visit(stack.removeLast());
|
| + }
|
| + }
|
| +
|
| + void push(Node node) {
|
| + assert(node != null);
|
| + stack.add(node);
|
| + }
|
| +
|
| + void pushAll(Iterable<Node> nodes) {
|
| + nodes.forEach(push);
|
| + }
|
| +
|
| + /************************* INTERIOR EXPRESSIONS *************************/
|
| + //
|
| + // These return nothing, and must push recursive children on the stack.
|
| +
|
| + void visitLetCont(LetCont node) {
|
| + pushAll(node.continuations);
|
| + push(node.body);
|
| + }
|
| +
|
| + void visitLetHandler(LetHandler node) {
|
| + push(node.handler);
|
| + push(node.body);
|
| + }
|
| +
|
| + void visitLetMutable(LetMutable node) {
|
| + visit(node.variable);
|
| + push(node.body);
|
| + }
|
| +
|
| + void visitLetPrim(LetPrim node) {
|
| + AbstractValue value = getValue(node.primitive);
|
| + if (node.primitive is! Constant && value.isConstant) {
|
| + // If the value is a known constant, compile it as a constant.
|
| + Constant newPrim = makeConstantPrimitive(value.constant);
|
| + newPrim.substituteFor(node.primitive);
|
| + RemovalVisitor.remove(node.primitive);
|
| + node.primitive = newPrim;
|
| + newPrim.parent = node;
|
| + } else {
|
| + Primitive newPrim = visit(node.primitive);
|
| + if (newPrim != null) {
|
| + 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.
|
| + // This would also be done by shrinking reductions, but usage analyses
|
| + // such as isAlwaysBoolified are more precise without the dead uses, so
|
| + // we prefer to remove them early.
|
| + RemovalVisitor.remove(node.primitive);
|
| + node.parent.body = node.body;
|
| + node.body.parent = node.parent;
|
| + }
|
| + }
|
| + push(node.body);
|
| + }
|
| +
|
| + void visitContinuation(Continuation node) {
|
| + if (node.isReturnContinuation) return;
|
| + // Process the continuation body.
|
| + // Note that the continuation body may have changed since the continuation
|
| + // was put on the stack (e.g. [visitInvokeContinuation] may do this).
|
| + push(node.body);
|
| }
|
|
|
| + /************************* TRANSFORMATION HELPERS *************************/
|
| +
|
| /// Sets parent pointers and computes types for the given subtree.
|
| void reanalyze(Node node) {
|
| new ParentVisitor().visit(node);
|
| @@ -623,6 +697,16 @@ class TransformingVisitor extends RecursiveVisitor {
|
| context.body = node;
|
| node.parent = context;
|
| }
|
| +
|
| + /// Binds [prim] before [node].
|
| + 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;
|
| + }
|
|
|
| /// Make a constant primitive for [constant] and set its entry in [values].
|
| Constant makeConstantPrimitive(ConstantValue constant) {
|
| @@ -648,23 +732,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| return letPrim;
|
| }
|
|
|
| - /// Side-effect free expressions with constant results are be replaced by:
|
| - ///
|
| - /// (LetPrim p = constant (InvokeContinuation k p)).
|
| - ///
|
| - /// The new expression will be visited.
|
| - ///
|
| - /// Returns true if the node was replaced.
|
| - bool constifyExpression(CallExpression node) {
|
| - Continuation continuation = node.continuation.definition;
|
| - ConstantValue constant = replacements[node];
|
| - if (constant == null) return false;
|
| - Constant primitive = makeConstantPrimitive(constant);
|
| - LetPrim letPrim = makeLetPrimInvoke(primitive, continuation);
|
| - replaceSubtree(node, letPrim);
|
| - visitLetPrim(letPrim);
|
| - return true;
|
| - }
|
| + /************************* TAIL EXPRESSIONS *************************/
|
|
|
| // A branch can be eliminated and replaced by an invocation if only one of
|
| // the possible continuations is reachable. Removal often leads to both dead
|
| @@ -684,13 +752,13 @@ class TransformingVisitor extends RecursiveVisitor {
|
| if (boolifiedValue == AbstractBool.True) {
|
| InvokeContinuation invoke = new InvokeContinuation(trueCont, []);
|
| replaceSubtree(node, invoke);
|
| - visitInvokeContinuation(invoke);
|
| + push(invoke);
|
| return;
|
| }
|
| if (boolifiedValue == AbstractBool.False) {
|
| InvokeContinuation invoke = new InvokeContinuation(falseCont, []);
|
| replaceSubtree(node, invoke);
|
| - visitInvokeContinuation(invoke);
|
| + push(invoke);
|
| return;
|
| }
|
|
|
| @@ -718,6 +786,36 @@ class TransformingVisitor extends RecursiveVisitor {
|
| }
|
| }
|
|
|
| + void visitInvokeContinuation(InvokeContinuation node) {
|
| + // Inline the single-use continuations. These are often introduced when
|
| + // specializing an invocation node. These would also be inlined by a later
|
| + // pass, but doing it here helps simplify pattern matching code, since the
|
| + // effective definition of a primitive can then be found without going
|
| + // through redundant InvokeContinuations.
|
| + Continuation cont = node.continuation.definition;
|
| + if (cont.hasExactlyOneUse &&
|
| + !cont.isReturnContinuation &&
|
| + !cont.isRecursive &&
|
| + !node.isEscapingTry) {
|
| + for (int i = 0; i < node.arguments.length; ++i) {
|
| + node.arguments[i].definition.useElementAsHint(cont.parameters[i].hint);
|
| + node.arguments[i].definition.substituteFor(cont.parameters[i]);
|
| + node.arguments[i].unlink();
|
| + }
|
| + node.continuation.unlink();
|
| + InteriorNode parent = node.parent;
|
| + Expression body = cont.body;
|
| + parent.body = body;
|
| + body.parent = parent;
|
| + cont.body = new Unreachable();
|
| + cont.body.parent = cont;
|
| + push(body);
|
| + }
|
| + }
|
| +
|
| +
|
| + /************************* CALL EXPRESSIONS *************************/
|
| +
|
| /// Replaces [node] with a more specialized instruction, if possible.
|
| ///
|
| /// Returns `true` if the node was replaced.
|
| @@ -731,7 +829,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| node.sourceInformation);
|
| LetPrim let = makeLetPrimInvoke(prim, cont);
|
| replaceSubtree(node, let);
|
| - visitLetPrim(let);
|
| + push(let);
|
| return true; // So returning early is more convenient.
|
| }
|
|
|
| @@ -829,7 +927,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| GetField get = new GetField(getDartReceiver(node), target);
|
| LetPrim let = makeLetPrimInvoke(get, cont);
|
| replaceSubtree(node, let);
|
| - visitLetPrim(let);
|
| + push(let);
|
| return true;
|
| } else {
|
| if (target.isFinal) return false;
|
| @@ -841,7 +939,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| getDartArgument(node, 0)));
|
| cps.invokeContinuation(cont);
|
| replaceSubtree(node, cps.result);
|
| - visit(cps.result);
|
| + push(cps.result);
|
| return true;
|
| }
|
| }
|
| @@ -934,7 +1032,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| CpsFragment cps = new CpsFragment(sourceInfo);
|
| cps.invokeContinuation(cont, [cps.letPrim(new GetLength(list))]);
|
| replaceSubtree(node, cps.result);
|
| - visit(cps.result);
|
| + push(cps.result);
|
| return true;
|
|
|
| case '[]':
|
| @@ -946,7 +1044,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| GetIndex get = cps.letPrim(new GetIndex(list, index));
|
| cps.invokeContinuation(cont, [get]);
|
| replaceSubtree(node, cps.result);
|
| - visit(cps.result);
|
| + push(cps.result);
|
| return true;
|
|
|
| case '[]=':
|
| @@ -962,7 +1060,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| cont.parameters.clear();
|
| cps.invokeContinuation(cont, []);
|
| replaceSubtree(node, cps.result);
|
| - visit(cps.result);
|
| + push(cps.result);
|
| return true;
|
|
|
| case 'forEach':
|
| @@ -1015,7 +1113,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| cps.continueLoop(loop, [addOne]);
|
|
|
| replaceSubtree(node, cps.result);
|
| - visit(cps.result);
|
| + push(cps.result);
|
| return true;
|
|
|
| case 'iterator':
|
| @@ -1166,9 +1264,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| insertBefore(iteratorCont.body, cps);
|
| InvokeContinuation invoke = new InvokeContinuation(iteratorCont, []);
|
| replaceSubtree(node, invoke);
|
| - visit(invoke);
|
| - // TODO(asgerf): A procedure for rewriting mutables into parameters
|
| - // might enable further optimizations after this.
|
| + push(invoke);
|
| return true;
|
|
|
| // TODO(asgerf): Rewrite 'add', 'removeLast', ...
|
| @@ -1243,7 +1339,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| node.sourceInformation);
|
| node.receiver.unlink();
|
| replaceSubtree(node, invoke, unlink: false);
|
| - visitInvokeStatic(invoke);
|
| + push(invoke);
|
| return true;
|
| }
|
| CallExpression tearOffInvoke = getCallWithResult(tearOff);
|
| @@ -1301,12 +1397,30 @@ class TransformingVisitor extends RecursiveVisitor {
|
| assert(isPure);
|
| }
|
|
|
| - visitInvokeMethod(invoke);
|
| + push(invoke);
|
| return true;
|
| }
|
| return false;
|
| }
|
|
|
| + /// Side-effect free expressions with constant results are be replaced by:
|
| + ///
|
| + /// (LetPrim p = constant (InvokeContinuation k p)).
|
| + ///
|
| + /// The new expression will be visited.
|
| + ///
|
| + /// Returns true if the node was replaced.
|
| + bool constifyExpression(CallExpression node) {
|
| + Continuation continuation = node.continuation.definition;
|
| + ConstantValue constant = replacements[node];
|
| + if (constant == null) return false;
|
| + Constant primitive = makeConstantPrimitive(constant);
|
| + LetPrim letPrim = makeLetPrimInvoke(primitive, continuation);
|
| + replaceSubtree(node, letPrim);
|
| + push(letPrim);
|
| + return true;
|
| + }
|
| +
|
| void visitInvokeMethod(InvokeMethod node) {
|
| if (constifyExpression(node)) return;
|
| if (specializeOperatorCall(node)) return;
|
| @@ -1316,7 +1430,6 @@ class TransformingVisitor extends RecursiveVisitor {
|
|
|
| AbstractValue receiver = getValue(node.receiver.definition);
|
| node.receiverIsNotNull = receiver.isDefinitelyNotNull;
|
| - super.visitInvokeMethod(node);
|
| }
|
|
|
| void visitTypeCast(TypeCast node) {
|
| @@ -1333,7 +1446,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| InvokeContinuation invoke =
|
| new InvokeContinuation(cont, <Primitive>[node.value.definition]);
|
| replaceSubtree(node, invoke);
|
| - visitInvokeContinuation(invoke);
|
| + push(invoke);
|
| return;
|
|
|
| case AbstractBool.False:
|
| @@ -1342,8 +1455,6 @@ class TransformingVisitor extends RecursiveVisitor {
|
| replaceSubtree(cont.body, new Unreachable());
|
| break;
|
| }
|
| -
|
| - super.visitTypeCast(node);
|
| }
|
|
|
| /// Specialize calls to internal static methods.
|
| @@ -1362,7 +1473,7 @@ class TransformingVisitor extends RecursiveVisitor {
|
| InvokeContinuation invoke =
|
| new InvokeContinuation(cont, <Primitive>[arg(0)]);
|
| replaceSubtree(node, invoke);
|
| - visitInvokeContinuation(invoke);
|
| + push(invoke);
|
| return true;
|
| }
|
| break;
|
| @@ -1381,14 +1492,13 @@ class TransformingVisitor extends RecursiveVisitor {
|
| return value == null ? new AbstractValue.nothing() : value;
|
| }
|
|
|
| - 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;
|
| - }
|
| +
|
| + /*************************** PRIMITIVES **************************/
|
| + //
|
| + // The visit method for a primitive may optionally return a new
|
| + // primitive. If non-null, the surrounding LetPrim will substitute it
|
| + // and bind the new primitive instead.
|
| + //
|
|
|
| void visitApplyBuiltinOperator(ApplyBuiltinOperator node) {
|
| DartString getString(AbstractValue value) {
|
| @@ -1489,73 +1599,6 @@ class TransformingVisitor extends RecursiveVisitor {
|
| node.objectIsNotNull = getValue(node.object.definition).isDefinitelyNotNull;
|
| }
|
|
|
| - void visitLetPrim(LetPrim node) {
|
| - AbstractValue value = getValue(node.primitive);
|
| - if (node.primitive is! Constant && value.isConstant) {
|
| - // If the value is a known constant, compile it as a constant.
|
| - Constant newPrim = makeConstantPrimitive(value.constant);
|
| - newPrim.substituteFor(node.primitive);
|
| - RemovalVisitor.remove(node.primitive);
|
| - node.primitive = newPrim;
|
| - newPrim.parent = node;
|
| - } else {
|
| - Primitive newPrim = visit(node.primitive);
|
| - if (newPrim != null) {
|
| - 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.
|
| - // This would also be done by shrinking reductions, but usage analyses
|
| - // such as isAlwaysBoolified are more precise without the dead uses, so
|
| - // we prefer to remove them early.
|
| - RemovalVisitor.remove(node.primitive);
|
| - node.parent.body = node.body;
|
| - node.body.parent = node.parent;
|
| - }
|
| - }
|
| - visit(node.body);
|
| - }
|
| -
|
| - void visitLetCont(LetCont node) {
|
| - // Visit body before continuations to ensure more information is available
|
| - // about the parameters. In particular, if this is a call continuation, its
|
| - // invocation should be specialized before the body is processed, because
|
| - // we specialize definitions before their uses.
|
| - visit(node.body);
|
| - node.continuations.forEach(visit);
|
| - }
|
| -
|
| - void visitInvokeContinuation(InvokeContinuation node) {
|
| - // Inline the single-use continuations. These are often introduced when
|
| - // specializing an invocation node. These would also be inlined by a later
|
| - // pass, but doing it here helps simplify pattern matching code, since the
|
| - // effective definition of a primitive can then be found without going
|
| - // through redundant InvokeContinuations.
|
| - Continuation cont = node.continuation.definition;
|
| - if (cont.hasExactlyOneUse &&
|
| - !cont.isReturnContinuation &&
|
| - !cont.isRecursive &&
|
| - !node.isEscapingTry) {
|
| - for (int i = 0; i < node.arguments.length; ++i) {
|
| - node.arguments[i].definition.useElementAsHint(cont.parameters[i].hint);
|
| - node.arguments[i].definition.substituteFor(cont.parameters[i]);
|
| - node.arguments[i].unlink();
|
| - }
|
| - node.continuation.unlink();
|
| - InteriorNode parent = node.parent;
|
| - Expression body = cont.body;
|
| - parent.body = body;
|
| - body.parent = parent;
|
| - cont.body = new Unreachable();
|
| - cont.body.parent = cont;
|
| - visit(body);
|
| - }
|
| - }
|
| -
|
| void visitInterceptor(Interceptor node) {
|
| // Filter out intercepted classes that do not match the input type.
|
| AbstractValue value = getValue(node.input.definition);
|
|
|