Support for loops in analyzer2dart.

BUG=
R=sigurdm@google.com

Review URL: https://codereview.chromium.org//683803003

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@41355 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
johnniwinther@google.com
2014-10-28 12:53:15 +00:00
parent f68daab3f9
commit fcc99ec62c
7 changed files with 323 additions and 151 deletions
@@ -302,4 +302,19 @@ class CpsGeneratingVisitor extends SemanticVisitor<ir.Node>
visitBlock(Block node) {
irBuilder.buildBlock(node.statements, build);
}
@override
visitForStatement(ForStatement node) {
// TODO(johnniwinther): Support `for` as a jump target.
SubbuildFunction buildInitializer;
if (node.variables != null) {
buildInitializer = subbuild(node.variables);
} else {
buildInitializer = subbuild(node.initialization);
}
irBuilder.buildFor(buildInitializer: buildInitializer,
buildCondition: subbuild(node.condition),
buildBody: subbuild(node.body),
buildUpdate: subbuildSequence(node.updaters));
}
}
+5 -1
View File
@@ -21,7 +21,11 @@ void compileToDart(Driver driver, ConvertedWorld convertedWorld) {
mainFunction: convertedWorld.mainFunction,
computeElementAst: (Element element) {
return DartBackend.createElementAst(
null, null, null, null, convertedWorld.getIr(element));
null, // No compiler.
null, // No tracer.
DART_CONSTANT_SYSTEM,
element,
convertedWorld.getIr(element));
},
shouldOutput: (_) => true,
isSafeToRemoveTypeDeclarations: (_) => false);
+44
View File
@@ -512,6 +512,50 @@ const TestSpec('''
main(a) {
new Deprecated("");
}
'''),
]),
const Group('For loop', const <TestSpec>[
const TestSpec('''
main() {
for (;;) {}
}
''', '''
main() {
while (true) {}
}
'''),
const TestSpec('''
main() {
for (int i = 0; i < 10; i = i + 1) {
print(i);
}
}
''', '''
main() {
var i = 0;
while (i < 10) {
print(i);
++i;
}
}
'''),
const TestSpec('''
main(i) {
for (i = 0; i < 10; i = i + 1) {
print(i);
}
}
''', '''
main(i) {
i = 0;
while (i < 10) {
print(i);
++i;
}
}
'''),
]),
];
+73
View File
@@ -813,6 +813,79 @@ main(a) {
(LetPrim v2 (Constant NullConstant))
(InvokeContinuation return v2))
(InvokeConstructor Deprecated v0 k0))
'''),
]),
const Group('For loop', const <TestSpec>[
const TestSpec('''
main() {
for (;;) {}
}
''', '''
(FunctionDefinition main ( return)
(LetCont* (k0)
(LetPrim v0 (Constant BoolConstant(true)))
(LetCont (k1)
(LetPrim v1 (Constant NullConstant))
(InvokeContinuation return v1))
(LetCont (k2)
(InvokeContinuation* k0 ))
(Branch (IsTrue v0) k2 k1))
(InvokeContinuation k0 ))
'''),
const TestSpec('''
main() {
for (int i = 0; i < 10; i = i + 1) {
print(i);
}
}
''', '''
(FunctionDefinition main ( return)
(LetPrim v0 (Constant IntConstant(0)))
(LetCont* (k0 v1)
(LetPrim v2 (Constant IntConstant(10)))
(LetCont (k1 v3)
(LetCont (k2)
(LetPrim v4 (Constant NullConstant))
(InvokeContinuation return v4))
(LetCont (k3)
(LetCont (k4 v5)
(LetPrim v6 (Constant IntConstant(1)))
(LetCont (k5 v7)
(InvokeContinuation* k0 v7))
(InvokeMethod v1 + v6 k5))
(InvokeStatic print v1 k4))
(Branch (IsTrue v3) k3 k2))
(InvokeMethod v1 < v2 k1))
(InvokeContinuation k0 v0))
'''),
const TestSpec('''
main(i) {
for (i = 0; i < 10; i = i + 1) {
print(i);
}
}
''', '''
(FunctionDefinition main (i return)
(LetPrim v0 (Constant IntConstant(0)))
(LetCont* (k0 v1)
(LetPrim v2 (Constant IntConstant(10)))
(LetCont (k1 v3)
(LetCont (k2)
(LetPrim v4 (Constant NullConstant))
(InvokeContinuation return v4))
(LetCont (k3)
(LetCont (k4 v5)
(LetPrim v6 (Constant IntConstant(1)))
(LetCont (k5 v7)
(InvokeContinuation* k0 v7))
(InvokeMethod v1 + v6 k5))
(InvokeStatic print v1 k4))
(Branch (IsTrue v3) k3 k2))
(InvokeMethod v1 < v2 k1))
(InvokeContinuation k0 v0))
'''),
]),
];
@@ -162,6 +162,16 @@ abstract class IrBuilderMixin<N> {
SubbuildFunction subbuild(N node) {
return (IrBuilder builder) => withBuilder(builder, () => build(node));
}
/// Returns a closure that takes an [IrBuilder] and builds the sequence of
/// [nodes] in its context using [build].
// TODO(johnniwinther): Type [nodes] as `Iterable<N>` when `NodeList` uses
// `List` instead of `Link`.
SubbuildFunction subbuildSequence(/*Iterable<N>*/ nodes) {
return (IrBuilder builder) {
return withBuilder(builder, () => builder.buildSequence(nodes, build));
};
}
}
/// Shared state between nested builders.
@@ -619,6 +629,154 @@ class IrBuilder {
}
}
/// Invoke a join-point continuation that contains arguments for all local
/// variables.
///
/// Given the continuation and a list of uninitialized invocations, fill
/// in each invocation with the continuation and appropriate arguments.
void invokeFullJoin(ir.Continuation join,
JumpCollector jumps,
{recursive: false}) {
join.isRecursive = recursive;
for (int i = 0; i < jumps.length; ++i) {
Environment currentEnvironment = jumps.environments[i];
ir.InvokeContinuation invoke = jumps.invocations[i];
invoke.continuation = new ir.Reference(join);
invoke.arguments = new List<ir.Reference>.generate(
join.parameters.length,
(i) => new ir.Reference(currentEnvironment[i]));
invoke.isRecursive = recursive;
}
}
/// Creates a for loop in which the initializer, condition, body, update are
/// created by [buildInitializer], [buildCondition], [buildBody] and
/// [buildUpdate], respectively.
///
/// The jump [target] is used to identify which `break` and `continue`
/// statements that have this `for` statement as their target.
void buildFor({SubbuildFunction buildInitializer,
SubbuildFunction buildCondition,
SubbuildFunction buildBody,
SubbuildFunction buildUpdate,
JumpTarget target}) {
assert(isOpen);
// For loops use four named continuations: the entry to the condition,
// the entry to the body, the loop exit, and the loop successor (break).
// The CPS translation of
// [[for (initializer; condition; update) body; successor]] is:
//
// [[initializer]];
// let cont loop(x, ...) =
// let prim cond = [[condition]] in
// let cont break() = [[successor]] in
// let cont exit() = break(v, ...) in
// let cont body() =
// let cont continue(x, ...) = [[update]]; loop(v, ...) in
// [[body]]; continue(v, ...) in
// branch cond (body, exit) in
// loop(v, ...)
//
// If there are no breaks in the body, the break continuation is inlined
// in the exit continuation (i.e., the translation of the successor
// statement occurs in the exit continuation). If there is only one
// invocation of the continue continuation (i.e., no continues in the
// body), the continue continuation is inlined in the body.
buildInitializer(this);
IrBuilder condBuilder = new IrBuilder.recursive(this);
ir.Primitive condition = buildCondition(condBuilder);
if (condition == null) {
// If the condition is empty then the body is entered unconditionally.
condition = condBuilder.buildBooleanLiteral(true);
}
JumpCollector breakCollector = new JumpCollector(target);
JumpCollector continueCollector = new JumpCollector(target);
state.breakCollectors.add(breakCollector);
state.continueCollectors.add(continueCollector);
IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
buildBody(bodyBuilder);
assert(state.breakCollectors.last == breakCollector);
assert(state.continueCollectors.last == continueCollector);
state.breakCollectors.removeLast();
state.continueCollectors.removeLast();
// The binding of the continue continuation should occur as late as
// possible, that is, at the nearest common ancestor of all the continue
// sites in the body. However, that is difficult to compute here, so it
// is instead placed just outside the body of the body continuation.
bool hasContinues = !continueCollector.isEmpty;
IrBuilder updateBuilder = hasContinues
? new IrBuilder.recursive(condBuilder)
: bodyBuilder;
buildUpdate(updateBuilder);
// Create body entry and loop exit continuations and a branch to them.
ir.Continuation bodyContinuation = new ir.Continuation([]);
ir.Continuation exitContinuation = new ir.Continuation([]);
ir.LetCont branch =
new ir.LetCont(exitContinuation,
new ir.LetCont(bodyContinuation,
new ir.Branch(new ir.IsTrue(condition),
bodyContinuation,
exitContinuation)));
// If there are breaks in the body, then there must be a join-point
// continuation for the normal exit and the breaks.
bool hasBreaks = !breakCollector.isEmpty;
ir.LetCont letJoin;
if (hasBreaks) {
letJoin = new ir.LetCont(null, branch);
condBuilder.add(letJoin);
condBuilder._current = branch;
} else {
condBuilder.add(branch);
}
ir.Continuation continueContinuation;
if (hasContinues) {
// If there are continues in the body, we need a named continue
// continuation as a join point.
continueContinuation = new ir.Continuation(updateBuilder._parameters);
if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
invokeFullJoin(continueContinuation, continueCollector);
}
ir.Continuation loopContinuation =
new ir.Continuation(condBuilder._parameters);
if (updateBuilder.isOpen) {
JumpCollector backEdges = new JumpCollector(null);
backEdges.addJump(updateBuilder);
invokeFullJoin(loopContinuation, backEdges, recursive: true);
}
// Fill in the body and possible continue continuation bodies. Do this
// only after it is guaranteed that they are not empty.
if (hasContinues) {
continueContinuation.body = updateBuilder._root;
bodyContinuation.body =
new ir.LetCont(continueContinuation, bodyBuilder._root);
} else {
bodyContinuation.body = bodyBuilder._root;
}
loopContinuation.body = condBuilder._root;
add(new ir.LetCont(loopContinuation,
new ir.InvokeContinuation(loopContinuation,
environment.index2value)));
if (hasBreaks) {
_current = branch;
environment = condBuilder.environment;
breakCollector.addJump(this);
letJoin.continuation = createJoin(environment.length, breakCollector);
_current = letJoin;
} else {
_current = condBuilder._current;
environment = condBuilder.environment;
}
}
/// Create a return statement `return value;` or `return;` if [value] is
/// null.
void buildReturn([ir.Primitive value]) {
@@ -635,16 +793,25 @@ class IrBuilder {
}
/// Create a blocks of [statements] by applying [build] to all reachable
/// statements.
/// statements. The first statement is assumed to be reachable.
// TODO(johnniwinther): Type [statements] as `Iterable` when `NodeList` uses
// `List` instead of `Link`.
void buildBlock(var statements, build(statement)) {
// Build(Block(stamements), C) = C'
// where C' = statements.fold(Build, C)
assert(isOpen);
for (var statement in statements) {
build(statement);
return buildSequence(statements, build);
}
/// Creates a sequence of [nodes] by applying [build] to all reachable nodes.
///
/// The first node in the sequence does not need to be reachable.
// TODO(johnniwinther): Type [nodes] as `Iterable` when `NodeList` uses
// `List` instead of `Link`.
void buildSequence(var nodes, build(node)) {
for (var node in nodes) {
if (!isOpen) return;
build(node);
}
}
@@ -210,28 +210,7 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
return null;
}
/// Invoke a join-point continuation that contains arguments for all local
/// variables.
///
/// Given the continuation and a list of uninitialized invocations, fill
/// in each invocation with the continuation and appropriate arguments.
void invokeFullJoin(ir.Continuation join,
JumpCollector jumps,
{recursive: false}) {
join.isRecursive = recursive;
for (int i = 0; i < jumps.length; ++i) {
Environment currentEnvironment = jumps.environments[i];
ir.InvokeContinuation invoke = jumps.invocations[i];
invoke.continuation = new ir.Reference(join);
invoke.arguments = new List<ir.Reference>.generate(
join.parameters.length,
(i) => new ir.Reference(currentEnvironment[i]));
invoke.isRecursive = recursive;
}
}
ir.Primitive visitFor(ast.For node) {
assert(irBuilder.isOpen);
visitFor(ast.For node) {
// TODO(kmillikin,sigurdm): Handle closure variables declared in a for-loop.
if (node.initializer is ast.VariableDefinitions) {
ast.VariableDefinitions definitions = node.initializer;
@@ -243,130 +222,16 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
}
}
// For loops use four named continuations: the entry to the condition,
// the entry to the body, the loop exit, and the loop successor (break).
// The CPS translation of
// [[for (initializer; condition; update) body; successor]] is:
//
// [[initializer]];
// let cont loop(x, ...) =
// let prim cond = [[condition]] in
// let cont break() = [[successor]] in
// let cont exit() = break(v, ...) in
// let cont body() =
// let cont continue(x, ...) = [[update]]; loop(v, ...) in
// [[body]]; continue(v, ...) in
// branch cond (body, exit) in
// loop(v, ...)
//
// If there are no breaks in the body, the break continuation is inlined
// in the exit continuation (i.e., the translation of the successor
// statement occurs in the exit continuation). If there is only one
// invocation of the continue continuation (i.e., no continues in the
// body), the continue continuation is inlined in the body.
if (node.initializer != null) visit(node.initializer);
IrBuilder condBuilder = new IrBuilder.recursive(irBuilder);
ir.Primitive condition;
if (node.condition == null) {
// If the condition is empty then the body is entered unconditionally.
condition = condBuilder.buildBooleanLiteral(true);
} else {
condition = withBuilder(condBuilder, () => visit(node.condition));
}
JumpTarget target = elements.getTargetDefinition(node);
JumpCollector breakCollector = new JumpCollector(target);
JumpCollector continueCollector = new JumpCollector(target);
irBuilder.state.breakCollectors.add(breakCollector);
irBuilder.state.continueCollectors.add(continueCollector);
IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
withBuilder(bodyBuilder, () => visit(node.body));
assert(irBuilder.state.breakCollectors.last == breakCollector);
assert(irBuilder.state.continueCollectors.last == continueCollector);
irBuilder.state.breakCollectors.removeLast();
irBuilder.state.continueCollectors.removeLast();
// The binding of the continue continuation should occur as late as
// possible, that is, at the nearest common ancestor of all the continue
// sites in the body. However, that is difficult to compute here, so it
// is instead placed just outside the body of the body continuation.
bool hasContinues = !continueCollector.isEmpty;
IrBuilder updateBuilder = hasContinues
? new IrBuilder.recursive(condBuilder)
: bodyBuilder;
for (ast.Node n in node.update) {
if (!updateBuilder.isOpen) break;
withBuilder(updateBuilder, () => visit(n));
}
// Create body entry and loop exit continuations and a branch to them.
ir.Continuation bodyContinuation = new ir.Continuation([]);
ir.Continuation exitContinuation = new ir.Continuation([]);
ir.LetCont branch =
new ir.LetCont(exitContinuation,
new ir.LetCont(bodyContinuation,
new ir.Branch(new ir.IsTrue(condition),
bodyContinuation,
exitContinuation)));
// If there are breaks in the body, then there must be a join-point
// continuation for the normal exit and the breaks.
bool hasBreaks = !breakCollector.isEmpty;
ir.LetCont letJoin;
if (hasBreaks) {
letJoin = new ir.LetCont(null, branch);
condBuilder.add(letJoin);
condBuilder._current = branch;
} else {
condBuilder.add(branch);
}
ir.Continuation continueContinuation;
if (hasContinues) {
// If there are continues in the body, we need a named continue
// continuation as a join point.
continueContinuation = new ir.Continuation(updateBuilder._parameters);
if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
invokeFullJoin(continueContinuation, continueCollector);
}
ir.Continuation loopContinuation =
new ir.Continuation(condBuilder._parameters);
if (updateBuilder.isOpen) {
JumpCollector backEdges = new JumpCollector(null);
backEdges.addJump(updateBuilder);
invokeFullJoin(loopContinuation, backEdges, recursive: true);
}
// Fill in the body and possible continue continuation bodies. Do this
// only after it is guaranteed that they are not empty.
if (hasContinues) {
continueContinuation.body = updateBuilder._root;
bodyContinuation.body =
new ir.LetCont(continueContinuation, bodyBuilder._root);
} else {
bodyContinuation.body = bodyBuilder._root;
}
loopContinuation.body = condBuilder._root;
irBuilder.add(new ir.LetCont(loopContinuation,
new ir.InvokeContinuation(loopContinuation,
irBuilder.environment.index2value)));
if (hasBreaks) {
irBuilder._current = branch;
irBuilder.environment = condBuilder.environment;
breakCollector.addJump(irBuilder);
letJoin.continuation =
irBuilder.createJoin(irBuilder.environment.length, breakCollector);
irBuilder._current = letJoin;
} else {
irBuilder._current = condBuilder._current;
irBuilder.environment = condBuilder.environment;
}
return null;
irBuilder.buildFor(
buildInitializer: subbuild(node.initializer),
buildCondition: subbuild(node.condition),
buildBody: subbuild(node.body),
buildUpdate: subbuildSequence(node.update),
target: target);
}
visitIf(ast.If node) {
visitIf(ast.If node) {
irBuilder.buildIf(
build(node.condition),
subbuild(node.thenPart),
@@ -439,7 +304,8 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
ir.Continuation loopContinuation =
new ir.Continuation(condBuilder._parameters);
if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
invokeFullJoin(loopContinuation, continueCollector, recursive: true);
irBuilder.invokeFullJoin(
loopContinuation, continueCollector, recursive: true);
bodyContinuation.body = bodyBuilder._root;
loopContinuation.body = condBuilder._root;
@@ -559,7 +425,8 @@ class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive>
ir.Continuation loopContinuation =
new ir.Continuation(condBuilder._parameters);
if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
invokeFullJoin(loopContinuation, continueCollector, recursive: true);
irBuilder.invokeFullJoin(
loopContinuation, continueCollector, recursive: true);
bodyContinuation.body = bodyBuilder._root;
loopContinuation.body = condBuilder._root;
@@ -667,8 +667,10 @@ class For extends Loop {
For asFor() => this;
Expression get condition {
if (conditionStatement is ExpressionStatement) {
return conditionStatement.asExpressionStatement().expression;
ExpressionStatement expressionStatement =
conditionStatement.asExpressionStatement();
if (expressionStatement != null) {
return expressionStatement.expression;
} else {
return null;
}