Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library cps_ir.cps_fragment; | |
| 6 | |
| 7 import 'cps_ir_nodes.dart'; | |
| 8 import '../constants/values.dart'; | |
| 9 import '../universe/universe.dart' show Selector; | |
| 10 import '../types/types.dart' show TypeMask; | |
| 11 import '../io/source_information.dart'; | |
| 12 import '../elements/elements.dart'; | |
| 13 | |
| 14 /// Builds a CPS fragment that can be plugged into another CPS term. | |
| 15 /// | |
| 16 /// A CPS fragment contains a CPS term, possibly with a "hole" in it denoting | |
| 17 /// where to insert new IR nodes. We say a fragment is "open" if it has such | |
| 18 /// 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
| |
| 19 /// | |
| 20 /// This class is designed for building non-trivial CPS terms in a readable and | |
| 21 /// non-error prone manner. It is not designed to manipulate existing IR nodes, | |
| 22 /// nor is it intended to shield the user from every complexity in the IR. | |
| 23 /// | |
| 24 /// EXAMPLES: | |
| 25 /// | |
| 26 /// Call `cont` with `obj.field + 1` as argument: | |
| 27 /// | |
| 28 /// CpsFragment cps = new CpsFragment(); | |
| 29 /// var fieldValue = cps.letPrim(new GetField(obj, field)); | |
| 30 /// var plusOne = cps.applyBuiltin(BuiltinOperator.NumAdd, | |
| 31 /// [fieldValue, cps.makeOne()]); | |
| 32 /// cps.invokeContinuation(cont, [plusOne]); | |
| 33 /// | |
| 34 /// If `condition` is true then invoke `cont1`, else `cont2`. | |
| 35 /// | |
| 36 /// cps.ifTrue(condition).invokeContinuation(cont1, []); | |
| 37 /// cps.invokeContinuation(cont2, []); | |
| 38 /// | |
| 39 /// If `condition` is true then invoke `cont` with a bound primitive: | |
| 40 /// | |
| 41 /// CpsFragment branch = cps.ifTrue(condition); | |
| 42 /// branch.invokeContinuation(cont, [branch.letPrim(arg)]); | |
| 43 /// | |
| 44 /// Loop and call a method until it returns false: | |
| 45 /// | |
| 46 /// Continuation loop = cps.beginLoop(); | |
| 47 /// var result = cps.invokeMethod(receiver, selector, ...); | |
| 48 /// cps.ifFalse(result).invokeContinuation(exit, []); | |
| 49 /// cps.continueLoop(loop); | |
| 50 /// | |
| 51 class CpsFragment { | |
| 52 /// The root of the IR built using this fragment. | |
| 53 Expression root; | |
| 54 | |
| 55 /// Node whose body is the hole in this CPS fragment. May be null. | |
| 56 InteriorNode context; | |
| 57 | |
| 58 /// Source information to attach to every IR node created in the fragment. | |
| 59 SourceInformation sourceInformation; | |
| 60 | |
| 61 CpsFragment([this.sourceInformation, this.context]); | |
| 62 | |
| 63 /// Asserts that the fragment is closed and returns the IR that was built. | |
| 64 Expression get result { | |
| 65 assert(context == null); | |
| 66 assert(root != null); | |
| 67 return root; | |
| 68 } | |
| 69 | |
| 70 /// Put the given expression into the fragment's hole. | |
| 71 /// | |
| 72 /// Afterwards the fragment is closed and cannot be extended until a new | |
| 73 /// [context] is set. | |
| 74 void put(Expression node) { | |
| 75 assert(root == null || context != null); // We must put the node somewhere. | |
| 76 if (root == null) { | |
| 77 root = node; | |
| 78 } | |
| 79 if (context != null) { | |
| 80 context.body = node; | |
| 81 } | |
| 82 context = null; | |
| 83 } | |
| 84 | |
| 85 /// Bind a primitive. Returns the same primitive for convenience. | |
| 86 Primitive letPrim(Primitive prim) { | |
| 87 assert(prim != null); | |
| 88 LetPrim let = new LetPrim(prim); | |
| 89 put(let); | |
| 90 context = let; | |
| 91 return prim; | |
| 92 } | |
| 93 | |
| 94 /// Bind a constant value. | |
| 95 Primitive makeConstant(ConstantValue constant) { | |
| 96 return letPrim(new Constant(constant)); | |
| 97 } | |
| 98 | |
| 99 Primitive makeZero() => makeConstant(new IntConstantValue(0)); | |
| 100 Primitive makeOne() => makeConstant(new IntConstantValue(1)); | |
| 101 Primitive makeNull() => makeConstant(new NullConstantValue()); | |
| 102 Primitive makeTrue() => makeConstant(new TrueConstantValue()); | |
| 103 Primitive makeFalse() => makeConstant(new FalseConstantValue()); | |
| 104 | |
| 105 /// Invoke a built-in operator. | |
| 106 Primitive applyBuiltin(BuiltinOperator op, List<Primitive> args) { | |
| 107 return letPrim(new ApplyBuiltinOperator(op, args)); | |
| 108 } | |
| 109 | |
| 110 /// Inserts an invocation. binds its continuation, and returns the | |
| 111 /// continuation parameter (i.e. the return value of the invocation). | |
| 112 /// | |
| 113 /// The continuation body becomes the new hole. | |
| 114 Parameter invokeMethod(Primitive receiver, | |
| 115 Selector selector, | |
| 116 TypeMask mask, | |
| 117 List<Primitive> arguments) { | |
| 118 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
| |
| 119 InvokeMethod invoke = | |
| 120 new InvokeMethod(receiver, selector, mask, arguments, cont, | |
| 121 sourceInformation); | |
| 122 put(new LetCont(cont, invoke)); | |
| 123 context = cont; | |
| 124 return cont.parameters.single; | |
| 125 } | |
| 126 | |
| 127 /// Inserts an invocation. binds its continuation, and returns the | |
| 128 /// continuation parameter (i.e. the return value of the invocation). | |
| 129 /// | |
| 130 /// The continuation body becomes the new hole. | |
| 131 Parameter invokeStatic(FunctionElement target, List<Primitive> arguments) { | |
| 132 Continuation cont = new Continuation([new Parameter(null)]); | |
| 133 InvokeStatic invoke = | |
| 134 new InvokeStatic(target, new Selector.fromElement(target), arguments, | |
| 135 cont, sourceInformation); | |
| 136 put(new LetCont(cont, invoke)); | |
| 137 context = cont; | |
| 138 return cont.parameters.single; | |
| 139 } | |
| 140 | |
| 141 /// Inserts an invocation to a static function that throws an error. | |
| 142 /// | |
| 143 /// This closes the fragment; no more nodes may be added. | |
| 144 void invokeStaticThrower(FunctionElement target, List<Primitive> arguments) { | |
| 145 invokeStatic(target, arguments); | |
| 146 put(new Unreachable()); | |
| 147 } | |
| 148 | |
| 149 /// Invoke a non-recursive continuation. | |
| 150 /// | |
| 151 /// This closes the fragment; no more nodes may be inserted. | |
| 152 void invokeContinuation(Continuation cont, [List<Primitive> arguments]) { | |
| 153 if (arguments == null) arguments = <Primitive>[]; | |
| 154 put(new InvokeContinuation(cont, arguments)); | |
| 155 } | |
| 156 | |
| 157 /// Build a loop with the given loop variables and initial values. | |
| 158 /// Call [continueLoop] with the returned continuation to iterate the loop. | |
| 159 /// | |
| 160 /// The loop body becomes the new hole. | |
| 161 Continuation beginLoop([List<Parameter> loopVars, | |
| 162 List<Primitive> initialValues]) { | |
| 163 if (initialValues == null) { | |
| 164 assert(loopVars == null); | |
| 165 loopVars = <Parameter>[]; | |
| 166 initialValues = <Primitive>[]; | |
| 167 } | |
| 168 Continuation cont = new Continuation(loopVars); | |
| 169 put(new LetCont(cont, new InvokeContinuation(cont, initialValues))); | |
| 170 context = cont; | |
| 171 return cont; | |
| 172 } | |
| 173 | |
| 174 /// Continue a loop started by [beginLoop]. | |
| 175 /// | |
| 176 /// This closes the fragment; no more nodes may be inserted. | |
| 177 void continueLoop(Continuation cont, [List<Primitive> updatedLoopVariables]) { | |
| 178 put(new InvokeContinuation(cont, updatedLoopVariables, isRecursive: true)); | |
| 179 } | |
| 180 | |
| 181 /// Branch on [condition]. | |
| 182 /// | |
| 183 /// Returns a new fragment for the 'then' branch. | |
| 184 /// | |
| 185 /// The 'else' branch becomes the new hole. | |
| 186 CpsFragment ifTrue(Primitive condition) { | |
| 187 Continuation trueCont = new Continuation([]); | |
| 188 Continuation falseCont = new Continuation([]); | |
| 189 put(new LetCont.two(trueCont, falseCont, | |
| 190 new Branch(new IsTrue(condition), trueCont, falseCont))); | |
| 191 context = falseCont; | |
| 192 return new CpsFragment(sourceInformation, trueCont); | |
| 193 } | |
| 194 | |
| 195 /// Branch on [condition]. | |
| 196 /// | |
| 197 /// Returns a new fragment for the 'else' branch. | |
| 198 /// | |
| 199 /// The 'then' branch becomes the new hole. | |
| 200 CpsFragment ifFalse(Primitive condition) { | |
| 201 Continuation trueCont = new Continuation([]); | |
| 202 Continuation falseCont = new Continuation([]); | |
| 203 put(new LetCont.two(trueCont, falseCont, | |
| 204 new Branch(new IsTrue(condition), trueCont, falseCont))); | |
| 205 context = trueCont; | |
| 206 return new CpsFragment(sourceInformation, falseCont); | |
| 207 } | |
| 208 | |
| 209 /// Create a new empty continuation and bind it here. | |
| 210 /// | |
| 211 /// Convenient for making a join point where multiple branches | |
| 212 /// meet later. | |
| 213 /// | |
| 214 /// The LetCont body becomes the new hole. | |
| 215 /// | |
| 216 /// Example use: | |
| 217 /// | |
| 218 /// Continuation fail = cps.letCont(); | |
| 219 /// | |
| 220 /// // Fail if something | |
| 221 /// cps.ifTrue(<condition>) | |
| 222 /// ..invokeMethod(<method>) | |
| 223 /// ..invokeContinuation(fail); | |
| 224 /// | |
| 225 /// // Fail if something else | |
| 226 /// cps.ifTrue(<anotherCondition>) | |
| 227 /// ..invokeMethod(<anotherMethod>) | |
| 228 /// ..invokeContinuation(fail); | |
| 229 /// | |
| 230 /// // Build the fail branch | |
| 231 /// cps.insideContinuation(fail) | |
| 232 /// ..invokeStaticThrower(...); | |
| 233 /// | |
| 234 /// // Go to the happy branch | |
| 235 /// cps.invokeContinuation(cont..) | |
| 236 /// | |
| 237 Continuation letCont([List<Parameter> parameters]) { | |
| 238 if (parameters == null) parameters = <Parameter>[]; | |
| 239 Continuation cont = new Continuation(parameters); | |
| 240 LetCont let = new LetCont(cont, null); | |
| 241 put(let); | |
| 242 context = let; | |
| 243 return cont; | |
| 244 } | |
| 245 | |
| 246 /// Returns a fragment whose context is the body of the given continuation. | |
| 247 /// | |
| 248 /// Does not change the state of this CPS fragment. | |
| 249 /// | |
| 250 /// Useful for building the body of a continuation created using [letCont]. | |
| 251 CpsFragment insideContinuation(Continuation cont) { | |
| 252 return new CpsFragment(sourceInformation, cont); | |
| 253 } | |
| 254 | |
| 255 /// Puts the given fragment into this one. | |
| 256 /// | |
| 257 /// If [other] was an open fragment, its hole becomes the new hole | |
| 258 /// in this fragment. | |
| 259 /// | |
| 260 /// [other] is reset to an empty fragment after this. | |
| 261 void append(CpsFragment other) { | |
| 262 if (other.root == null) return; | |
| 263 put(other.root); | |
| 264 context = other.context; | |
| 265 other.context = null; | |
| 266 other.root = null; | |
| 267 } | |
| 268 } | |
| OLD | NEW |