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

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

Issue 1251083002: dart2js cps: Avoid deep recursion using trampolines and basic blocks. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Rebase Created 5 years, 5 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 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);
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/redundant_join.dart ('k') | pkg/compiler/lib/src/js_backend/codegen/unsugar.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698