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

Unified Diff: pkg/compiler/lib/src/tree_ir/tree_ir_builder.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/tree_ir/tree_ir_builder.dart
diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
index 606a53f3b1dda40038e367df66604c0db92e6710..921e83076c7d3f78f44510685380dfb70e49cc8b 100644
--- a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
+++ b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
@@ -10,6 +10,8 @@ import '../cps_ir/cps_ir_nodes.dart' as cps_ir;
import '../util/util.dart' show CURRENT_ELEMENT_SPANNABLE;
import 'tree_ir_nodes.dart';
+typedef Statement NodeCallback(Statement next);
+
/**
* Builder translates from CPS-based IR to direct-style Tree.
*
@@ -42,7 +44,7 @@ import 'tree_ir_nodes.dart';
* particular, intermediate values and blocks used for local control flow are
* still all named.
*/
-class Builder implements cps_ir.Visitor<Node> {
+class Builder implements cps_ir.Visitor/*<NodeCallback|Node>*/ {
final dart2js.InternalErrorFunction internalError;
final Map<cps_ir.Primitive, Variable> primitive2variable =
@@ -109,6 +111,10 @@ class Builder implements cps_ir.Visitor<Node> {
return new VariableUse(getVariable(reference.definition));
}
+ Label getLabel(cps_ir.Continuation cont) {
+ return labels.putIfAbsent(cont, () => new Label());
+ }
+
Variable addFunctionParameter(cps_ir.Definition variable) {
if (variable is cps_ir.Parameter) {
return getVariable(variable);
@@ -130,7 +136,7 @@ class Builder implements cps_ir.Visitor<Node> {
node.parameters.map(addFunctionParameter).toList();
returnContinuation = node.returnContinuation;
phiTempVar = new Variable(node.element, null);
- Statement body = visit(node.body);
+ Statement body = translateExpression(node.body);
return new FunctionDefinition(node.element, parameters, body);
}
@@ -147,19 +153,6 @@ class Builder implements cps_ir.Visitor<Node> {
growable: false);
}
- Statement buildContinuationAssignment(
- cps_ir.Parameter parameter,
- Expression argument,
- Statement buildRest()) {
- Expression expr;
- if (parameter.hasAtLeastOneUse) {
- expr = new Assign(getVariable(parameter), argument);
- } else {
- expr = argument;
- }
- return new ExpressionStatement(expr, buildRest());
- }
-
/// Simultaneously assigns each argument to the corresponding parameter,
/// then continues at the statement created by [buildRest].
Statement buildPhiAssignments(
@@ -252,112 +245,134 @@ class Builder implements cps_ir.Visitor<Node> {
return first;
}
- visit(cps_ir.Node node) => node.accept(this);
-
- unexpectedNode(cps_ir.Node node) {
- internalError(CURRENT_ELEMENT_SPANNABLE, 'Unexpected IR node: $node');
- }
-
- Expression visitSetField(cps_ir.SetField node) {
- return new SetField(getVariableUse(node.object),
- node.field,
- getVariableUse(node.value));
- }
-
- Expression visitInterceptor(cps_ir.Interceptor node) {
- return new Interceptor(
- getVariableUse(node.input),
- node.interceptedClasses,
- node.sourceInformation);
- }
-
- Expression visitCreateInstance(cps_ir.CreateInstance node) {
- return new CreateInstance(
- node.classElement,
- translateArguments(node.arguments),
- translateArguments(node.typeInformation),
- node.sourceInformation);
- }
-
- Expression visitGetField(cps_ir.GetField node) {
- return new GetField(getVariableUse(node.object), node.field,
- objectIsNotNull: node.objectIsNotNull);
+ visit(cps_ir.Node node) => throw 'Use translateXXX instead of visit';
+
+ /// Translates a CPS expression into a tree statement.
+ ///
+ /// To avoid deep recursion, we traverse each basic blocks without
+ /// recursion.
+ ///
+ /// Non-tail expressions evaluate to a callback to be invoked once the
+ /// successor statement has been constructed. These callbacks are stored
+ /// in a stack until the block's tail expression has been translated.
+ Statement translateExpression(cps_ir.Expression node) {
+ List<NodeCallback> stack = <NodeCallback>[];
+ while (node is! cps_ir.TailExpression) {
+ stack.add(node.accept(this));
+ node = node.next;
+ }
+ Statement result = node.accept(this); // Translate the tail expression.
+ for (NodeCallback fun in stack.reversed) {
+ result = fun(result);
+ }
+ return result;
}
- Expression visitCreateBox(cps_ir.CreateBox node) {
- return new CreateBox();
+ /// Translates a CPS primitive to a tree expression.
+ ///
+ /// This simply calls the visit method for the primitive.
+ Expression translatePrimitive(cps_ir.Primitive prim) {
+ return prim.accept(this);
}
- visitCreateInvocationMirror(cps_ir.CreateInvocationMirror node) {
- return new CreateInvocationMirror(
- node.selector,
- translateArguments(node.arguments));
+ /// Translates a condition to a tree expression.
+ Expression translateCondition(cps_ir.Condition condition) {
+ cps_ir.IsTrue isTrue = condition;
+ return getVariableUse(isTrue.value);
}
- // Executable definitions are not visited directly. They have 'build'
- // functions as entry points.
- visitFunctionDefinition(cps_ir.FunctionDefinition node) {
- return unexpectedNode(node);
- }
+ /************************ INTERIOR EXPRESSIONS ************************/
+ //
+ // Visit methods for interior expressions must return a function:
+ //
+ // (Statement next) => <result statement>
+ //
- Statement visitLetPrim(cps_ir.LetPrim node) {
+ NodeCallback visitLetPrim(cps_ir.LetPrim node) => (Statement next) {
Variable variable = getVariable(node.primitive);
- Expression value = visit(node.primitive);
+ Expression value = translatePrimitive(node.primitive);
if (node.primitive.hasAtLeastOneUse) {
- return Assign.makeStatement(variable, value, visit(node.body));
+ return Assign.makeStatement(variable, value, next);
} else {
- return new ExpressionStatement(value, visit(node.body));
+ return new ExpressionStatement(value, next);
}
- }
-
- Statement visitLetCont(cps_ir.LetCont node) {
- // Introduce labels for continuations that need them.
+ };
+
+ // Continuations are bound at the same level, but they have to be
+ // translated as if nested. This is because the body can invoke any
+ // of them from anywhere, so it must be nested inside all of them.
+ //
+ // The continuation bodies are not always translated directly here because
+ // they may have been already translated:
+ // * For singly-used continuations, the continuation's body is
+ // translated at the site of the continuation invocation.
+ // * For recursive continuations, there is a single non-recursive
+ // invocation. The continuation's body is translated at the site
+ // of the non-recursive continuation invocation.
+ // See [visitInvokeContinuation] for the implementation.
+ NodeCallback visitLetCont(cps_ir.LetCont node) => (Statement next) {
for (cps_ir.Continuation continuation in node.continuations) {
- if (continuation.hasMultipleUses || continuation.isRecursive) {
- labels[continuation] = new Label();
- }
- }
- Statement body = visit(node.body);
- // Continuations are bound at the same level, but they have to be
- // translated as if nested. This is because the body can invoke any
- // of them from anywhere, so it must be nested inside all of them.
- //
- // The continuation bodies are not always translated directly here because
- // they may have been already translated:
- // * For singly-used continuations, the continuation's body is
- // translated at the site of the continuation invocation.
- // * For recursive continuations, there is a single non-recursive
- // invocation. The continuation's body is translated at the site
- // of the non-recursive continuation invocation.
- // See visitInvokeContinuation for the implementation.
- Statement current = body;
- for (cps_ir.Continuation continuation in node.continuations.reversed) {
+ // This happens after the body of the LetCont has been translated.
+ // Labels are created on-demand if the continuation could not be inlined,
+ // so the existence of the label indicates if a labeled statement should
+ // be emitted.
Label label = labels[continuation];
if (label != null && !continuation.isRecursive) {
- current =
- new LabeledStatement(label, current, visit(continuation.body));
+ // Recursively build the body. We only do this for join continuations,
+ // so we should not risk overly deep recursion.
+ next = new LabeledStatement(
+ label,
+ next,
+ translateExpression(continuation.body));
}
}
- return current;
- }
+ return next;
+ };
- Statement visitLetHandler(cps_ir.LetHandler node) {
- Statement tryBody = visit(node.body);
+ NodeCallback visitLetHandler(cps_ir.LetHandler node) => (Statement next) {
List<Variable> catchParameters =
node.handler.parameters.map(getVariable).toList();
- Statement catchBody = visit(node.handler.body);
- return new Try(tryBody, catchParameters, catchBody);
+ Statement catchBody = translateExpression(node.handler.body);
+ return new Try(next, catchParameters, catchBody);
+ };
+
+ NodeCallback visitLetMutable(cps_ir.LetMutable node) {
+ Variable variable = addMutableVariable(node.variable);
+ Expression value = getVariableUse(node.value);
+ return (Statement next) => Assign.makeStatement(variable, value, next);
+ }
+
+
+ /************************ CALL EXPRESSIONS ************************/
+ //
+ // Visit methods for call expressions must return a function:
+ //
+ // (Statement next) => <result statement>
+ //
+ // The result statement must include an assignment to the continuation
+ // parameter, if the parameter is used.
+ //
+
+ NodeCallback makeCallExpression(cps_ir.CallExpression call,
+ Expression expression) {
+ return (Statement next) {
+ cps_ir.Parameter result = call.continuation.definition.parameters.single;
+ if (result.hasAtLeastOneUse) {
+ return Assign.makeStatement(getVariable(result), expression, next);
+ } else {
+ return new ExpressionStatement(expression, next);
+ }
+ };
}
- Statement visitInvokeStatic(cps_ir.InvokeStatic node) {
- // Calls are translated to direct style.
+ NodeCallback visitInvokeStatic(cps_ir.InvokeStatic node) {
List<Expression> arguments = translateArguments(node.arguments);
Expression invoke = new InvokeStatic(node.target, node.selector, arguments,
node.sourceInformation);
- return continueWithExpression(node.continuation, invoke);
+ return makeCallExpression(node, invoke);
}
- Statement visitInvokeMethod(cps_ir.InvokeMethod node) {
+ NodeCallback visitInvokeMethod(cps_ir.InvokeMethod node) {
InvokeMethod invoke = new InvokeMethod(
getVariableUse(node.receiver),
node.selector,
@@ -365,84 +380,82 @@ class Builder implements cps_ir.Visitor<Node> {
translateArguments(node.arguments),
node.sourceInformation);
invoke.receiverIsNotNull = node.receiverIsNotNull;
- return continueWithExpression(node.continuation, invoke);
+ return makeCallExpression(node, invoke);
}
- Statement visitInvokeMethodDirectly(cps_ir.InvokeMethodDirectly node) {
+ NodeCallback visitInvokeMethodDirectly(cps_ir.InvokeMethodDirectly node) {
Expression receiver = getVariableUse(node.receiver);
List<Expression> arguments = translateArguments(node.arguments);
Expression invoke = new InvokeMethodDirectly(receiver, node.target,
node.selector, arguments, node.sourceInformation);
- return continueWithExpression(node.continuation, invoke);
+ return makeCallExpression(node, invoke);
}
- Statement visitThrow(cps_ir.Throw node) {
+ NodeCallback visitTypeCast(cps_ir.TypeCast node) {
Expression value = getVariableUse(node.value);
- return new Throw(value);
- }
-
- Statement visitRethrow(cps_ir.Rethrow node) {
- return new Rethrow();
+ List<Expression> typeArgs = translateArguments(node.typeArguments);
+ Expression expression =
+ new TypeOperator(value, node.type, typeArgs, isTypeTest: false);
+ return makeCallExpression(node, expression);
}
- Statement visitUnreachable(cps_ir.Unreachable node) {
- return new Unreachable();
+ NodeCallback visitInvokeConstructor(cps_ir.InvokeConstructor node) {
+ List<Expression> arguments = translateArguments(node.arguments);
+ Expression invoke = new InvokeConstructor(
+ node.type,
+ node.target,
+ node.selector,
+ arguments,
+ node.sourceInformation);
+ return makeCallExpression(node, invoke);
}
- Statement continueWithExpression(cps_ir.Reference continuation,
- Expression expression) {
- cps_ir.Continuation cont = continuation.definition;
- if (cont == returnContinuation) {
- return new Return(expression);
+ NodeCallback visitForeignCode(cps_ir.ForeignCode node) {
+ if (node.codeTemplate.isExpression) {
+ Expression foreignCode = new ForeignExpression(
+ node.codeTemplate,
+ node.type,
+ node.arguments.map(getVariableUse).toList(growable: false),
+ node.nativeBehavior,
+ node.dependency);
+ return makeCallExpression(node, foreignCode);
} else {
- assert(cont.parameters.length == 1);
- Function nextBuilder = cont.hasExactlyOneUse ?
- () => visit(cont.body) : () => new Break(labels[cont]);
- return buildContinuationAssignment(cont.parameters.single, expression,
- nextBuilder);
+ return (Statement next) {
+ assert(next is Unreachable); // We are not using the `next` statement.
+ return new ForeignStatement(
+ node.codeTemplate,
+ node.type,
+ node.arguments.map(getVariableUse).toList(growable: false),
+ node.nativeBehavior,
+ node.dependency);
+ };
}
}
- Statement visitLetMutable(cps_ir.LetMutable node) {
- Variable variable = addMutableVariable(node.variable);
- Expression value = getVariableUse(node.value);
- Statement body = visit(node.body);
- return Assign.makeStatement(variable, value, body);
+ NodeCallback visitGetLazyStatic(cps_ir.GetLazyStatic node) {
+ // In the tree IR, GetStatic handles lazy fields because we do not need
+ // as fine-grained control over side effects.
+ GetStatic value = new GetStatic(node.element, node.sourceInformation);
+ return makeCallExpression(node, value);
}
- Expression visitGetMutable(cps_ir.GetMutable node) {
- return getMutableVariableUse(node.variable);
- }
- Expression visitSetMutable(cps_ir.SetMutable node) {
- Variable variable = getMutableVariable(node.variable.definition);
- Expression value = getVariableUse(node.value);
- return new Assign(variable, value);
- }
+ /************************** TAIL EXPRESSIONS **************************/
+ //
+ // Visit methods for tail expressions must return a statement directly
+ // (not a function like interior and call expressions).
- Statement visitTypeCast(cps_ir.TypeCast node) {
+ Statement visitThrow(cps_ir.Throw node) {
Expression value = getVariableUse(node.value);
- List<Expression> typeArgs = translateArguments(node.typeArguments);
- Expression expression =
- new TypeOperator(value, node.type, typeArgs, isTypeTest: false);
- return continueWithExpression(node.continuation, expression);
+ return new Throw(value);
}
- Expression visitTypeTest(cps_ir.TypeTest node) {
- Expression value = getVariableUse(node.value);
- List<Expression> typeArgs = translateArguments(node.typeArguments);
- return new TypeOperator(value, node.type, typeArgs, isTypeTest: true);
+ Statement visitRethrow(cps_ir.Rethrow node) {
+ return new Rethrow();
}
- Statement visitInvokeConstructor(cps_ir.InvokeConstructor node) {
- List<Expression> arguments = translateArguments(node.arguments);
- Expression invoke = new InvokeConstructor(
- node.type,
- node.target,
- node.selector,
- arguments,
- node.sourceInformation);
- return continueWithExpression(node.continuation, invoke);
+ Statement visitUnreachable(cps_ir.Unreachable node) {
+ return new Unreachable();
}
Statement visitInvokeContinuation(cps_ir.InvokeContinuation node) {
@@ -473,35 +486,85 @@ class Builder implements cps_ir.Visitor<Node> {
// - Translate the recursive invocations to Continue.
if (cont.isRecursive) {
return node.isRecursive
- ? new Continue(labels[cont])
- : new WhileTrue(labels[cont], visit(cont.body));
+ ? new Continue(getLabel(cont))
+ : new WhileTrue(getLabel(cont),
+ translateExpression(cont.body));
} else {
- if (cont.hasExactlyOneUse) {
- if (!node.isEscapingTry) {
- return visit(cont.body);
- }
- labels[cont] = new Label();
- }
- return new Break(labels[cont]);
+ return cont.hasExactlyOneUse && !node.isEscapingTry
+ ? translateExpression(cont.body)
+ : new Break(getLabel(cont));
}
});
}
}
Statement visitBranch(cps_ir.Branch node) {
- Expression condition = visit(node.condition);
+ Expression condition = translateCondition(node.condition);
Statement thenStatement, elseStatement;
cps_ir.Continuation cont = node.trueContinuation.definition;
assert(cont.parameters.isEmpty);
- thenStatement =
- cont.hasExactlyOneUse ? visit(cont.body) : new Break(labels[cont]);
+ thenStatement = cont.hasExactlyOneUse
+ ? translateExpression(cont.body)
+ : new Break(labels[cont]);
cont = node.falseContinuation.definition;
assert(cont.parameters.isEmpty);
- elseStatement =
- cont.hasExactlyOneUse ? visit(cont.body) : new Break(labels[cont]);
+ elseStatement = cont.hasExactlyOneUse
+ ? translateExpression(cont.body)
+ : new Break(labels[cont]);
return new If(condition, thenStatement, elseStatement);
}
+
+ /************************** PRIMITIVES **************************/
+ //
+ // Visit methods for primitives must return an expression.
+ //
+
+ Expression visitSetField(cps_ir.SetField node) {
+ return new SetField(getVariableUse(node.object),
+ node.field,
+ getVariableUse(node.value));
+ }
+
+ Expression visitInterceptor(cps_ir.Interceptor node) {
+ return new Interceptor(getVariableUse(node.input),
+ node.interceptedClasses,
+ node.sourceInformation);
+ }
+
+ Expression visitCreateInstance(cps_ir.CreateInstance node) {
+ return new CreateInstance(
+ node.classElement,
+ translateArguments(node.arguments),
+ translateArguments(node.typeInformation),
+ node.sourceInformation);
+ }
+
+ Expression visitGetField(cps_ir.GetField node) {
+ return new GetField(getVariableUse(node.object), node.field,
+ objectIsNotNull: node.objectIsNotNull);
+ }
+
+ Expression visitCreateBox(cps_ir.CreateBox node) {
+ return new CreateBox();
+ }
+
+ Expression visitCreateInvocationMirror(cps_ir.CreateInvocationMirror node) {
+ return new CreateInvocationMirror(
+ node.selector,
+ translateArguments(node.arguments));
+ }
+
+ Expression visitGetMutable(cps_ir.GetMutable node) {
+ return getMutableVariableUse(node.variable);
+ }
+
+ Expression visitSetMutable(cps_ir.SetMutable node) {
+ Variable variable = getMutableVariable(node.variable.definition);
+ Expression value = getVariableUse(node.value);
+ return new Assign(variable, value);
+ }
+
Expression visitConstant(cps_ir.Constant node) {
return new Constant(node.value, sourceInformation: node.sourceInformation);
}
@@ -532,28 +595,6 @@ class Builder implements cps_ir.Visitor<Node> {
return new FunctionExpression(def);
}
- visitParameter(cps_ir.Parameter node) {
- // Continuation parameters are not visited (continuations themselves are
- // not visited yet).
- unexpectedNode(node);
- }
-
- visitContinuation(cps_ir.Continuation node) {
- // Until continuations with multiple uses are supported, they are not
- // visited.
- unexpectedNode(node);
- }
-
- visitMutableVariable(cps_ir.MutableVariable node) {
- // These occur as parameters or bound by LetMutable. They are not visited
- // directly.
- unexpectedNode(node);
- }
-
- Expression visitIsTrue(cps_ir.IsTrue node) {
- return getVariableUse(node.value);
- }
-
Expression visitReifyRuntimeType(cps_ir.ReifyRuntimeType node) {
return new ReifyRuntimeType(
getVariableUse(node.value), node.sourceInformation);
@@ -566,22 +607,20 @@ class Builder implements cps_ir.Visitor<Node> {
node.sourceInformation);
}
- @override
- Node visitTypeExpression(cps_ir.TypeExpression node) {
+ Expression visitTypeExpression(cps_ir.TypeExpression node) {
return new TypeExpression(
node.dartType,
node.arguments.map(getVariableUse).toList());
}
- Expression visitGetStatic(cps_ir.GetStatic node) {
- return new GetStatic(node.element, node.sourceInformation);
+ Expression visitTypeTest(cps_ir.TypeTest node) {
+ Expression value = getVariableUse(node.value);
+ List<Expression> typeArgs = translateArguments(node.typeArguments);
+ return new TypeOperator(value, node.type, typeArgs, isTypeTest: true);
}
- Statement visitGetLazyStatic(cps_ir.GetLazyStatic node) {
- // In the tree IR, GetStatic handles lazy fields because tree
- // expressions are allowed to have side effects.
- GetStatic value = new GetStatic(node.element, node.sourceInformation);
- return continueWithExpression(node.continuation, value);
+ Expression visitGetStatic(cps_ir.GetStatic node) {
+ return new GetStatic(node.element, node.sourceInformation);
}
Expression visitSetStatic(cps_ir.SetStatic node) {
@@ -599,26 +638,6 @@ class Builder implements cps_ir.Visitor<Node> {
translateArguments(node.arguments));
}
- Statement visitForeignCode(cps_ir.ForeignCode node) {
- if (node.codeTemplate.isExpression) {
- Expression foreignCode = new ForeignExpression(
- node.codeTemplate,
- node.type,
- node.arguments.map(getVariableUse).toList(growable: false),
- node.nativeBehavior,
- node.dependency);
- return continueWithExpression(node.continuation, foreignCode);
- } else {
- assert(node.continuation.definition.body is cps_ir.Unreachable);
- return new ForeignStatement(
- node.codeTemplate,
- node.type,
- node.arguments.map(getVariableUse).toList(growable: false),
- node.nativeBehavior,
- node.dependency);
- }
- }
-
Expression visitGetLength(cps_ir.GetLength node) {
return new GetLength(getVariableUse(node.object));
}
@@ -633,5 +652,19 @@ class Builder implements cps_ir.Visitor<Node> {
getVariableUse(node.index),
getVariableUse(node.value));
}
+
+ /********** UNUSED VISIT METHODS *************/
+
+ unexpectedNode(cps_ir.Node node) {
+ internalError(CURRENT_ELEMENT_SPANNABLE, 'Unexpected IR node: $node');
+ }
+
+ visitFunctionDefinition(cps_ir.FunctionDefinition node) {
+ unexpectedNode(node);
+ }
+ visitParameter(cps_ir.Parameter node) => unexpectedNode(node);
+ visitContinuation(cps_ir.Continuation node) => unexpectedNode(node);
+ visitMutableVariable(cps_ir.MutableVariable node) => unexpectedNode(node);
+ visitIsTrue(cps_ir.IsTrue node) => unexpectedNode(node);
}
« no previous file with comments | « pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart ('k') | pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698