Chromium Code Reviews| Index: pkg/compiler/lib/src/cps_ir/cps_fragment.dart |
| diff --git a/pkg/compiler/lib/src/cps_ir/cps_fragment.dart b/pkg/compiler/lib/src/cps_ir/cps_fragment.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..700979ebc56aeefc2c3ad6e5c4908870b119c501 |
| --- /dev/null |
| +++ b/pkg/compiler/lib/src/cps_ir/cps_fragment.dart |
| @@ -0,0 +1,268 @@ |
| +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +library cps_ir.cps_fragment; |
| + |
| +import 'cps_ir_nodes.dart'; |
| +import '../constants/values.dart'; |
| +import '../universe/universe.dart' show Selector; |
| +import '../types/types.dart' show TypeMask; |
| +import '../io/source_information.dart'; |
| +import '../elements/elements.dart'; |
| + |
| +/// Builds a CPS fragment that can be plugged into another CPS term. |
| +/// |
| +/// A CPS fragment contains a CPS term, possibly with a "hole" in it denoting |
| +/// where to insert new IR nodes. We say a fragment is "open" if it has such |
| +/// a hole. |
|
karlklose
2015/07/08 08:39:24
Maybe add: "Otherwise, the fragment is 'closed' an
asgerf
2015/07/08 10:28:37
Added comment about "closed".
The return type isn
|
| +/// |
| +/// This class is designed for building non-trivial CPS terms in a readable and |
| +/// non-error prone manner. It is not designed to manipulate existing IR nodes, |
| +/// nor is it intended to shield the user from every complexity in the IR. |
| +/// |
| +/// EXAMPLES: |
| +/// |
| +/// Call `cont` with `obj.field + 1` as argument: |
| +/// |
| +/// CpsFragment cps = new CpsFragment(); |
| +/// var fieldValue = cps.letPrim(new GetField(obj, field)); |
| +/// var plusOne = cps.applyBuiltin(BuiltinOperator.NumAdd, |
| +/// [fieldValue, cps.makeOne()]); |
| +/// cps.invokeContinuation(cont, [plusOne]); |
| +/// |
| +/// If `condition` is true then invoke `cont1`, else `cont2`. |
| +/// |
| +/// cps.ifTrue(condition).invokeContinuation(cont1, []); |
| +/// cps.invokeContinuation(cont2, []); |
| +/// |
| +/// If `condition` is true then invoke `cont` with a bound primitive: |
| +/// |
| +/// CpsFragment branch = cps.ifTrue(condition); |
| +/// branch.invokeContinuation(cont, [branch.letPrim(arg)]); |
| +/// |
| +/// Loop and call a method until it returns false: |
| +/// |
| +/// Continuation loop = cps.beginLoop(); |
| +/// var result = cps.invokeMethod(receiver, selector, ...); |
| +/// cps.ifFalse(result).invokeContinuation(exit, []); |
| +/// cps.continueLoop(loop); |
| +/// |
| +class CpsFragment { |
| + /// The root of the IR built using this fragment. |
| + Expression root; |
| + |
| + /// Node whose body is the hole in this CPS fragment. May be null. |
| + InteriorNode context; |
| + |
| + /// Source information to attach to every IR node created in the fragment. |
| + SourceInformation sourceInformation; |
| + |
| + CpsFragment([this.sourceInformation, this.context]); |
| + |
| + /// Asserts that the fragment is closed and returns the IR that was built. |
| + Expression get result { |
| + assert(context == null); |
| + assert(root != null); |
| + return root; |
| + } |
| + |
| + /// Put the given expression into the fragment's hole. |
| + /// |
| + /// Afterwards the fragment is closed and cannot be extended until a new |
| + /// [context] is set. |
| + void put(Expression node) { |
| + assert(root == null || context != null); // We must put the node somewhere. |
| + if (root == null) { |
| + root = node; |
| + } |
| + if (context != null) { |
| + context.body = node; |
| + } |
| + context = null; |
| + } |
| + |
| + /// Bind a primitive. Returns the same primitive for convenience. |
| + Primitive letPrim(Primitive prim) { |
| + assert(prim != null); |
| + LetPrim let = new LetPrim(prim); |
| + put(let); |
| + context = let; |
| + return prim; |
| + } |
| + |
| + /// Bind a constant value. |
| + Primitive makeConstant(ConstantValue constant) { |
| + return letPrim(new Constant(constant)); |
| + } |
| + |
| + Primitive makeZero() => makeConstant(new IntConstantValue(0)); |
| + Primitive makeOne() => makeConstant(new IntConstantValue(1)); |
| + Primitive makeNull() => makeConstant(new NullConstantValue()); |
| + Primitive makeTrue() => makeConstant(new TrueConstantValue()); |
| + Primitive makeFalse() => makeConstant(new FalseConstantValue()); |
| + |
| + /// Invoke a built-in operator. |
| + Primitive applyBuiltin(BuiltinOperator op, List<Primitive> args) { |
| + return letPrim(new ApplyBuiltinOperator(op, args)); |
| + } |
| + |
| + /// Inserts an invocation. binds its continuation, and returns the |
| + /// continuation parameter (i.e. the return value of the invocation). |
| + /// |
| + /// The continuation body becomes the new hole. |
| + Parameter invokeMethod(Primitive receiver, |
| + Selector selector, |
| + TypeMask mask, |
| + List<Primitive> arguments) { |
| + Continuation cont = new Continuation([new Parameter(null)]); |
|
karlklose
2015/07/08 08:39:24
Add type arguments to list literals? (multiple pla
asgerf
2015/07/08 10:28:37
I've added some type arguments in places where it
|
| + InvokeMethod invoke = |
| + new InvokeMethod(receiver, selector, mask, arguments, cont, |
| + sourceInformation); |
| + put(new LetCont(cont, invoke)); |
| + context = cont; |
| + return cont.parameters.single; |
| + } |
| + |
| + /// Inserts an invocation. binds its continuation, and returns the |
| + /// continuation parameter (i.e. the return value of the invocation). |
| + /// |
| + /// The continuation body becomes the new hole. |
| + Parameter invokeStatic(FunctionElement target, List<Primitive> arguments) { |
| + Continuation cont = new Continuation([new Parameter(null)]); |
| + InvokeStatic invoke = |
| + new InvokeStatic(target, new Selector.fromElement(target), arguments, |
| + cont, sourceInformation); |
| + put(new LetCont(cont, invoke)); |
| + context = cont; |
| + return cont.parameters.single; |
| + } |
| + |
| + /// Inserts an invocation to a static function that throws an error. |
| + /// |
| + /// This closes the fragment; no more nodes may be added. |
| + void invokeStaticThrower(FunctionElement target, List<Primitive> arguments) { |
| + invokeStatic(target, arguments); |
| + put(new Unreachable()); |
| + } |
| + |
| + /// Invoke a non-recursive continuation. |
| + /// |
| + /// This closes the fragment; no more nodes may be inserted. |
| + void invokeContinuation(Continuation cont, [List<Primitive> arguments]) { |
| + if (arguments == null) arguments = <Primitive>[]; |
| + put(new InvokeContinuation(cont, arguments)); |
| + } |
| + |
| + /// Build a loop with the given loop variables and initial values. |
| + /// Call [continueLoop] with the returned continuation to iterate the loop. |
| + /// |
| + /// The loop body becomes the new hole. |
| + Continuation beginLoop([List<Parameter> loopVars, |
| + List<Primitive> initialValues]) { |
| + if (initialValues == null) { |
| + assert(loopVars == null); |
| + loopVars = <Parameter>[]; |
| + initialValues = <Primitive>[]; |
| + } |
| + Continuation cont = new Continuation(loopVars); |
| + put(new LetCont(cont, new InvokeContinuation(cont, initialValues))); |
| + context = cont; |
| + return cont; |
| + } |
| + |
| + /// Continue a loop started by [beginLoop]. |
| + /// |
| + /// This closes the fragment; no more nodes may be inserted. |
| + void continueLoop(Continuation cont, [List<Primitive> updatedLoopVariables]) { |
| + put(new InvokeContinuation(cont, updatedLoopVariables, isRecursive: true)); |
| + } |
| + |
| + /// Branch on [condition]. |
| + /// |
| + /// Returns a new fragment for the 'then' branch. |
| + /// |
| + /// The 'else' branch becomes the new hole. |
| + CpsFragment ifTrue(Primitive condition) { |
| + Continuation trueCont = new Continuation([]); |
| + Continuation falseCont = new Continuation([]); |
| + put(new LetCont.two(trueCont, falseCont, |
| + new Branch(new IsTrue(condition), trueCont, falseCont))); |
| + context = falseCont; |
| + return new CpsFragment(sourceInformation, trueCont); |
| + } |
| + |
| + /// Branch on [condition]. |
| + /// |
| + /// Returns a new fragment for the 'else' branch. |
| + /// |
| + /// The 'then' branch becomes the new hole. |
| + CpsFragment ifFalse(Primitive condition) { |
| + Continuation trueCont = new Continuation([]); |
| + Continuation falseCont = new Continuation([]); |
| + put(new LetCont.two(trueCont, falseCont, |
| + new Branch(new IsTrue(condition), trueCont, falseCont))); |
| + context = trueCont; |
| + return new CpsFragment(sourceInformation, falseCont); |
| + } |
| + |
| + /// Create a new empty continuation and bind it here. |
| + /// |
| + /// Convenient for making a join point where multiple branches |
| + /// meet later. |
| + /// |
| + /// The LetCont body becomes the new hole. |
| + /// |
| + /// Example use: |
| + /// |
| + /// Continuation fail = cps.letCont(); |
| + /// |
| + /// // Fail if something |
| + /// cps.ifTrue(<condition>) |
| + /// ..invokeMethod(<method>) |
| + /// ..invokeContinuation(fail); |
| + /// |
| + /// // Fail if something else |
| + /// cps.ifTrue(<anotherCondition>) |
| + /// ..invokeMethod(<anotherMethod>) |
| + /// ..invokeContinuation(fail); |
| + /// |
| + /// // Build the fail branch |
| + /// cps.insideContinuation(fail) |
| + /// ..invokeStaticThrower(...); |
| + /// |
| + /// // Go to the happy branch |
| + /// cps.invokeContinuation(cont..) |
| + /// |
| + Continuation letCont([List<Parameter> parameters]) { |
| + if (parameters == null) parameters = <Parameter>[]; |
| + Continuation cont = new Continuation(parameters); |
| + LetCont let = new LetCont(cont, null); |
| + put(let); |
| + context = let; |
| + return cont; |
| + } |
| + |
| + /// Returns a fragment whose context is the body of the given continuation. |
| + /// |
| + /// Does not change the state of this CPS fragment. |
| + /// |
| + /// Useful for building the body of a continuation created using [letCont]. |
| + CpsFragment insideContinuation(Continuation cont) { |
| + return new CpsFragment(sourceInformation, cont); |
| + } |
| + |
| + /// Puts the given fragment into this one. |
| + /// |
| + /// If [other] was an open fragment, its hole becomes the new hole |
| + /// in this fragment. |
| + /// |
| + /// [other] is reset to an empty fragment after this. |
| + void append(CpsFragment other) { |
| + if (other.root == null) return; |
| + put(other.root); |
| + context = other.context; |
| + other.context = null; |
| + other.root = null; |
| + } |
| +} |