From fcc99ec62c19c8bb7af6aaef2793b82bbc7e396c Mon Sep 17 00:00:00 2001 From: "johnniwinther@google.com" Date: Tue, 28 Oct 2014 12:53:15 +0000 Subject: [PATCH] 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 --- pkg/analyzer2dart/lib/src/cps_generator.dart | 15 ++ pkg/analyzer2dart/lib/src/dart_backend.dart | 6 +- pkg/analyzer2dart/test/end2end_data.dart | 44 +++++ pkg/analyzer2dart/test/sexpr_data.dart | 73 ++++++++ .../implementation/cps_ir/cps_ir_builder.dart | 173 +++++++++++++++++- .../cps_ir/cps_ir_builder_visitor.dart | 157 ++-------------- .../compiler/implementation/tree/nodes.dart | 6 +- 7 files changed, 323 insertions(+), 151 deletions(-) diff --git a/pkg/analyzer2dart/lib/src/cps_generator.dart b/pkg/analyzer2dart/lib/src/cps_generator.dart index 9601cb82087..a8c7698a88e 100644 --- a/pkg/analyzer2dart/lib/src/cps_generator.dart +++ b/pkg/analyzer2dart/lib/src/cps_generator.dart @@ -302,4 +302,19 @@ class CpsGeneratingVisitor extends SemanticVisitor 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)); + } } diff --git a/pkg/analyzer2dart/lib/src/dart_backend.dart b/pkg/analyzer2dart/lib/src/dart_backend.dart index 840951551d5..ab0f6448a12 100644 --- a/pkg/analyzer2dart/lib/src/dart_backend.dart +++ b/pkg/analyzer2dart/lib/src/dart_backend.dart @@ -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); diff --git a/pkg/analyzer2dart/test/end2end_data.dart b/pkg/analyzer2dart/test/end2end_data.dart index 1f68c2d1c7e..455f790a757 100644 --- a/pkg/analyzer2dart/test/end2end_data.dart +++ b/pkg/analyzer2dart/test/end2end_data.dart @@ -512,6 +512,50 @@ const TestSpec(''' main(a) { new Deprecated(""); } +'''), + ]), + + const Group('For loop', const [ + 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; + } +} '''), ]), ]; diff --git a/pkg/analyzer2dart/test/sexpr_data.dart b/pkg/analyzer2dart/test/sexpr_data.dart index d5e4f47e6b7..bf1f08ae210 100644 --- a/pkg/analyzer2dart/test/sexpr_data.dart +++ b/pkg/analyzer2dart/test/sexpr_data.dart @@ -813,6 +813,79 @@ main(a) { (LetPrim v2 (Constant NullConstant)) (InvokeContinuation return v2)) (InvokeConstructor Deprecated v0 k0)) +'''), + ]), + + const Group('For loop', const [ + 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)) '''), ]), ]; diff --git a/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart b/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart index c12cd4da410..5a006dc8340 100644 --- a/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart +++ b/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart @@ -162,6 +162,16 @@ abstract class IrBuilderMixin { 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` when `NodeList` uses + // `List` instead of `Link`. + SubbuildFunction subbuildSequence(/*Iterable*/ 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.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); } } diff --git a/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder_visitor.dart b/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder_visitor.dart index 25058e065f9..085a1c66361 100644 --- a/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder_visitor.dart +++ b/sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder_visitor.dart @@ -210,28 +210,7 @@ class IrBuilderVisitor extends ResolvedVisitor 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.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 } } - // 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.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.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; diff --git a/sdk/lib/_internal/compiler/implementation/tree/nodes.dart b/sdk/lib/_internal/compiler/implementation/tree/nodes.dart index 505d2835c64..5bb147e3b8d 100644 --- a/sdk/lib/_internal/compiler/implementation/tree/nodes.dart +++ b/sdk/lib/_internal/compiler/implementation/tree/nodes.dart @@ -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; }