OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library tree_ir_builder; | 5 library tree_ir_builder; |
6 | 6 |
7 import '../dart2jslib.dart' as dart2js; | 7 import '../dart2jslib.dart' as dart2js; |
8 import '../elements/elements.dart'; | 8 import '../elements/elements.dart'; |
9 import '../cps_ir/cps_ir_nodes.dart' as cps_ir; | 9 import '../cps_ir/cps_ir_nodes.dart' as cps_ir; |
10 import '../util/util.dart' show CURRENT_ELEMENT_SPANNABLE; | 10 import '../util/util.dart' show CURRENT_ELEMENT_SPANNABLE; |
11 import 'tree_ir_nodes.dart'; | 11 import 'tree_ir_nodes.dart'; |
12 | 12 |
| 13 typedef Statement NodeCallback(Statement next); |
| 14 |
13 /** | 15 /** |
14 * Builder translates from CPS-based IR to direct-style Tree. | 16 * Builder translates from CPS-based IR to direct-style Tree. |
15 * | 17 * |
16 * A call `Invoke(fun, cont, args)`, where cont is a singly-referenced | 18 * A call `Invoke(fun, cont, args)`, where cont is a singly-referenced |
17 * non-exit continuation `Cont(v, body)` is translated into a direct-style call | 19 * non-exit continuation `Cont(v, body)` is translated into a direct-style call |
18 * whose value is bound in the continuation body: | 20 * whose value is bound in the continuation body: |
19 * | 21 * |
20 * `LetVal(v, Invoke(fun, args), body)` | 22 * `LetVal(v, Invoke(fun, args), body)` |
21 * | 23 * |
22 * and the continuation definition is eliminated. A similar translation is | 24 * and the continuation definition is eliminated. A similar translation is |
(...skipping 12 matching lines...) Expand all Loading... |
35 * `LetLabel(L, v, body)` | 37 * `LetLabel(L, v, body)` |
36 * | 38 * |
37 * Block arguments are later replaced with data flow during the Tree-to-Tree | 39 * Block arguments are later replaced with data flow during the Tree-to-Tree |
38 * translation out of SSA. Jumps are eliminated during the Tree-to-Tree | 40 * translation out of SSA. Jumps are eliminated during the Tree-to-Tree |
39 * control-flow recognition. | 41 * control-flow recognition. |
40 * | 42 * |
41 * Otherwise, the output of Builder looks very much like the input. In | 43 * Otherwise, the output of Builder looks very much like the input. In |
42 * particular, intermediate values and blocks used for local control flow are | 44 * particular, intermediate values and blocks used for local control flow are |
43 * still all named. | 45 * still all named. |
44 */ | 46 */ |
45 class Builder implements cps_ir.Visitor<Node> { | 47 class Builder implements cps_ir.Visitor/*<NodeCallback|Node>*/ { |
46 final dart2js.InternalErrorFunction internalError; | 48 final dart2js.InternalErrorFunction internalError; |
47 | 49 |
48 final Map<cps_ir.Primitive, Variable> primitive2variable = | 50 final Map<cps_ir.Primitive, Variable> primitive2variable = |
49 <cps_ir.Primitive, Variable>{}; | 51 <cps_ir.Primitive, Variable>{}; |
50 final Map<cps_ir.MutableVariable, Variable> mutable2variable = | 52 final Map<cps_ir.MutableVariable, Variable> mutable2variable = |
51 <cps_ir.MutableVariable, Variable>{}; | 53 <cps_ir.MutableVariable, Variable>{}; |
52 | 54 |
53 // Continuations with more than one use are replaced with Tree labels. This | 55 // Continuations with more than one use are replaced with Tree labels. This |
54 // is the mapping from continuations to labels. | 56 // is the mapping from continuations to labels. |
55 final Map<cps_ir.Continuation, Label> labels = <cps_ir.Continuation, Label>{}; | 57 final Map<cps_ir.Continuation, Label> labels = <cps_ir.Continuation, Label>{}; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
102 /// referred to by [reference]. | 104 /// referred to by [reference]. |
103 /// This increments the reference count for the given variable, so the | 105 /// This increments the reference count for the given variable, so the |
104 /// returned expression must be used in the tree. | 106 /// returned expression must be used in the tree. |
105 Expression getVariableUse(cps_ir.Reference<cps_ir.Primitive> reference) { | 107 Expression getVariableUse(cps_ir.Reference<cps_ir.Primitive> reference) { |
106 if (thisParameter != null && reference.definition == thisParameter) { | 108 if (thisParameter != null && reference.definition == thisParameter) { |
107 return new This(); | 109 return new This(); |
108 } | 110 } |
109 return new VariableUse(getVariable(reference.definition)); | 111 return new VariableUse(getVariable(reference.definition)); |
110 } | 112 } |
111 | 113 |
| 114 Label getLabel(cps_ir.Continuation cont) { |
| 115 return labels.putIfAbsent(cont, () => new Label()); |
| 116 } |
| 117 |
112 Variable addFunctionParameter(cps_ir.Definition variable) { | 118 Variable addFunctionParameter(cps_ir.Definition variable) { |
113 if (variable is cps_ir.Parameter) { | 119 if (variable is cps_ir.Parameter) { |
114 return getVariable(variable); | 120 return getVariable(variable); |
115 } else { | 121 } else { |
116 return addMutableVariable(variable as cps_ir.MutableVariable) | 122 return addMutableVariable(variable as cps_ir.MutableVariable) |
117 ..isCaptured = true; | 123 ..isCaptured = true; |
118 } | 124 } |
119 } | 125 } |
120 | 126 |
121 FunctionDefinition buildFunction(cps_ir.FunctionDefinition node) { | 127 FunctionDefinition buildFunction(cps_ir.FunctionDefinition node) { |
122 currentElement = node.element; | 128 currentElement = node.element; |
123 if (parent != null) { | 129 if (parent != null) { |
124 // Local function's 'this' refers to enclosing method's 'this' | 130 // Local function's 'this' refers to enclosing method's 'this' |
125 thisParameter = parent.thisParameter; | 131 thisParameter = parent.thisParameter; |
126 } else { | 132 } else { |
127 thisParameter = node.thisParameter; | 133 thisParameter = node.thisParameter; |
128 } | 134 } |
129 List<Variable> parameters = | 135 List<Variable> parameters = |
130 node.parameters.map(addFunctionParameter).toList(); | 136 node.parameters.map(addFunctionParameter).toList(); |
131 returnContinuation = node.returnContinuation; | 137 returnContinuation = node.returnContinuation; |
132 phiTempVar = new Variable(node.element, null); | 138 phiTempVar = new Variable(node.element, null); |
133 Statement body = visit(node.body); | 139 Statement body = translateExpression(node.body); |
134 return new FunctionDefinition(node.element, parameters, body); | 140 return new FunctionDefinition(node.element, parameters, body); |
135 } | 141 } |
136 | 142 |
137 /// Returns a list of variables corresponding to the arguments to a method | 143 /// Returns a list of variables corresponding to the arguments to a method |
138 /// call or similar construct. | 144 /// call or similar construct. |
139 /// | 145 /// |
140 /// The `readCount` for these variables will be incremented. | 146 /// The `readCount` for these variables will be incremented. |
141 /// | 147 /// |
142 /// The list will be typed as a list of [Expression] to allow inplace updates | 148 /// The list will be typed as a list of [Expression] to allow inplace updates |
143 /// on the list during the rewrite phases. | 149 /// on the list during the rewrite phases. |
144 List<Expression> translateArguments(List<cps_ir.Reference> args) { | 150 List<Expression> translateArguments(List<cps_ir.Reference> args) { |
145 return new List<Expression>.generate(args.length, | 151 return new List<Expression>.generate(args.length, |
146 (int index) => getVariableUse(args[index]), | 152 (int index) => getVariableUse(args[index]), |
147 growable: false); | 153 growable: false); |
148 } | 154 } |
149 | 155 |
150 Statement buildContinuationAssignment( | |
151 cps_ir.Parameter parameter, | |
152 Expression argument, | |
153 Statement buildRest()) { | |
154 Expression expr; | |
155 if (parameter.hasAtLeastOneUse) { | |
156 expr = new Assign(getVariable(parameter), argument); | |
157 } else { | |
158 expr = argument; | |
159 } | |
160 return new ExpressionStatement(expr, buildRest()); | |
161 } | |
162 | |
163 /// Simultaneously assigns each argument to the corresponding parameter, | 156 /// Simultaneously assigns each argument to the corresponding parameter, |
164 /// then continues at the statement created by [buildRest]. | 157 /// then continues at the statement created by [buildRest]. |
165 Statement buildPhiAssignments( | 158 Statement buildPhiAssignments( |
166 List<cps_ir.Parameter> parameters, | 159 List<cps_ir.Parameter> parameters, |
167 List<Expression> arguments, | 160 List<Expression> arguments, |
168 Statement buildRest()) { | 161 Statement buildRest()) { |
169 assert(parameters.length == arguments.length); | 162 assert(parameters.length == arguments.length); |
170 // We want a parallel assignment to all parameters simultaneously. | 163 // We want a parallel assignment to all parameters simultaneously. |
171 // Since we do not have parallel assignments in dart_tree, we must linearize | 164 // Since we do not have parallel assignments in dart_tree, we must linearize |
172 // the assignments without attempting to read a previously-overwritten | 165 // the assignments without attempting to read a previously-overwritten |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
245 } | 238 } |
246 | 239 |
247 if (first == null) { | 240 if (first == null) { |
248 first = buildRest(); | 241 first = buildRest(); |
249 } else { | 242 } else { |
250 current.next = buildRest(); | 243 current.next = buildRest(); |
251 } | 244 } |
252 return first; | 245 return first; |
253 } | 246 } |
254 | 247 |
255 visit(cps_ir.Node node) => node.accept(this); | 248 visit(cps_ir.Node node) => throw 'Use translateXXX instead of visit'; |
256 | 249 |
257 unexpectedNode(cps_ir.Node node) { | 250 /// Translates a CPS expression into a tree statement. |
258 internalError(CURRENT_ELEMENT_SPANNABLE, 'Unexpected IR node: $node'); | 251 /// |
| 252 /// To avoid deep recursion, we traverse each basic blocks without |
| 253 /// recursion. |
| 254 /// |
| 255 /// Non-tail expressions evaluate to a callback to be invoked once the |
| 256 /// successor statement has been constructed. These callbacks are stored |
| 257 /// in a stack until the block's tail expression has been translated. |
| 258 Statement translateExpression(cps_ir.Expression node) { |
| 259 List<NodeCallback> stack = <NodeCallback>[]; |
| 260 while (node is! cps_ir.TailExpression) { |
| 261 stack.add(node.accept(this)); |
| 262 node = node.next; |
| 263 } |
| 264 Statement result = node.accept(this); // Translate the tail expression. |
| 265 for (NodeCallback fun in stack.reversed) { |
| 266 result = fun(result); |
| 267 } |
| 268 return result; |
259 } | 269 } |
260 | 270 |
261 Expression visitSetField(cps_ir.SetField node) { | 271 /// Translates a CPS primitive to a tree expression. |
262 return new SetField(getVariableUse(node.object), | 272 /// |
263 node.field, | 273 /// This simply calls the visit method for the primitive. |
264 getVariableUse(node.value)); | 274 Expression translatePrimitive(cps_ir.Primitive prim) { |
265 } | 275 return prim.accept(this); |
266 | |
267 Expression visitInterceptor(cps_ir.Interceptor node) { | |
268 return new Interceptor( | |
269 getVariableUse(node.input), | |
270 node.interceptedClasses, | |
271 node.sourceInformation); | |
272 } | 276 } |
273 | 277 |
274 Expression visitCreateInstance(cps_ir.CreateInstance node) { | 278 /// Translates a condition to a tree expression. |
275 return new CreateInstance( | 279 Expression translateCondition(cps_ir.Condition condition) { |
276 node.classElement, | 280 cps_ir.IsTrue isTrue = condition; |
277 translateArguments(node.arguments), | 281 return getVariableUse(isTrue.value); |
278 translateArguments(node.typeInformation), | |
279 node.sourceInformation); | |
280 } | 282 } |
281 | 283 |
282 Expression visitGetField(cps_ir.GetField node) { | 284 /************************ INTERIOR EXPRESSIONS ************************/ |
283 return new GetField(getVariableUse(node.object), node.field, | 285 // |
284 objectIsNotNull: node.objectIsNotNull); | 286 // Visit methods for interior expressions must return a function: |
| 287 // |
| 288 // (Statement next) => <result statement> |
| 289 // |
| 290 |
| 291 NodeCallback visitLetPrim(cps_ir.LetPrim node) => (Statement next) { |
| 292 Variable variable = getVariable(node.primitive); |
| 293 Expression value = translatePrimitive(node.primitive); |
| 294 if (node.primitive.hasAtLeastOneUse) { |
| 295 return Assign.makeStatement(variable, value, next); |
| 296 } else { |
| 297 return new ExpressionStatement(value, next); |
| 298 } |
| 299 }; |
| 300 |
| 301 // Continuations are bound at the same level, but they have to be |
| 302 // translated as if nested. This is because the body can invoke any |
| 303 // of them from anywhere, so it must be nested inside all of them. |
| 304 // |
| 305 // The continuation bodies are not always translated directly here because |
| 306 // they may have been already translated: |
| 307 // * For singly-used continuations, the continuation's body is |
| 308 // translated at the site of the continuation invocation. |
| 309 // * For recursive continuations, there is a single non-recursive |
| 310 // invocation. The continuation's body is translated at the site |
| 311 // of the non-recursive continuation invocation. |
| 312 // See [visitInvokeContinuation] for the implementation. |
| 313 NodeCallback visitLetCont(cps_ir.LetCont node) => (Statement next) { |
| 314 for (cps_ir.Continuation continuation in node.continuations) { |
| 315 // This happens after the body of the LetCont has been translated. |
| 316 // Labels are created on-demand if the continuation could not be inlined, |
| 317 // so the existence of the label indicates if a labeled statement should |
| 318 // be emitted. |
| 319 Label label = labels[continuation]; |
| 320 if (label != null && !continuation.isRecursive) { |
| 321 // Recursively build the body. We only do this for join continuations, |
| 322 // so we should not risk overly deep recursion. |
| 323 next = new LabeledStatement( |
| 324 label, |
| 325 next, |
| 326 translateExpression(continuation.body)); |
| 327 } |
| 328 } |
| 329 return next; |
| 330 }; |
| 331 |
| 332 NodeCallback visitLetHandler(cps_ir.LetHandler node) => (Statement next) { |
| 333 List<Variable> catchParameters = |
| 334 node.handler.parameters.map(getVariable).toList(); |
| 335 Statement catchBody = translateExpression(node.handler.body); |
| 336 return new Try(next, catchParameters, catchBody); |
| 337 }; |
| 338 |
| 339 NodeCallback visitLetMutable(cps_ir.LetMutable node) { |
| 340 Variable variable = addMutableVariable(node.variable); |
| 341 Expression value = getVariableUse(node.value); |
| 342 return (Statement next) => Assign.makeStatement(variable, value, next); |
285 } | 343 } |
286 | 344 |
287 Expression visitCreateBox(cps_ir.CreateBox node) { | 345 |
288 return new CreateBox(); | 346 /************************ CALL EXPRESSIONS ************************/ |
| 347 // |
| 348 // Visit methods for call expressions must return a function: |
| 349 // |
| 350 // (Statement next) => <result statement> |
| 351 // |
| 352 // The result statement must include an assignment to the continuation |
| 353 // parameter, if the parameter is used. |
| 354 // |
| 355 |
| 356 NodeCallback makeCallExpression(cps_ir.CallExpression call, |
| 357 Expression expression) { |
| 358 return (Statement next) { |
| 359 cps_ir.Parameter result = call.continuation.definition.parameters.single; |
| 360 if (result.hasAtLeastOneUse) { |
| 361 return Assign.makeStatement(getVariable(result), expression, next); |
| 362 } else { |
| 363 return new ExpressionStatement(expression, next); |
| 364 } |
| 365 }; |
289 } | 366 } |
290 | 367 |
291 visitCreateInvocationMirror(cps_ir.CreateInvocationMirror node) { | 368 NodeCallback visitInvokeStatic(cps_ir.InvokeStatic node) { |
292 return new CreateInvocationMirror( | |
293 node.selector, | |
294 translateArguments(node.arguments)); | |
295 } | |
296 | |
297 // Executable definitions are not visited directly. They have 'build' | |
298 // functions as entry points. | |
299 visitFunctionDefinition(cps_ir.FunctionDefinition node) { | |
300 return unexpectedNode(node); | |
301 } | |
302 | |
303 Statement visitLetPrim(cps_ir.LetPrim node) { | |
304 Variable variable = getVariable(node.primitive); | |
305 Expression value = visit(node.primitive); | |
306 if (node.primitive.hasAtLeastOneUse) { | |
307 return Assign.makeStatement(variable, value, visit(node.body)); | |
308 } else { | |
309 return new ExpressionStatement(value, visit(node.body)); | |
310 } | |
311 } | |
312 | |
313 Statement visitLetCont(cps_ir.LetCont node) { | |
314 // Introduce labels for continuations that need them. | |
315 for (cps_ir.Continuation continuation in node.continuations) { | |
316 if (continuation.hasMultipleUses || continuation.isRecursive) { | |
317 labels[continuation] = new Label(); | |
318 } | |
319 } | |
320 Statement body = visit(node.body); | |
321 // Continuations are bound at the same level, but they have to be | |
322 // translated as if nested. This is because the body can invoke any | |
323 // of them from anywhere, so it must be nested inside all of them. | |
324 // | |
325 // The continuation bodies are not always translated directly here because | |
326 // they may have been already translated: | |
327 // * For singly-used continuations, the continuation's body is | |
328 // translated at the site of the continuation invocation. | |
329 // * For recursive continuations, there is a single non-recursive | |
330 // invocation. The continuation's body is translated at the site | |
331 // of the non-recursive continuation invocation. | |
332 // See visitInvokeContinuation for the implementation. | |
333 Statement current = body; | |
334 for (cps_ir.Continuation continuation in node.continuations.reversed) { | |
335 Label label = labels[continuation]; | |
336 if (label != null && !continuation.isRecursive) { | |
337 current = | |
338 new LabeledStatement(label, current, visit(continuation.body)); | |
339 } | |
340 } | |
341 return current; | |
342 } | |
343 | |
344 Statement visitLetHandler(cps_ir.LetHandler node) { | |
345 Statement tryBody = visit(node.body); | |
346 List<Variable> catchParameters = | |
347 node.handler.parameters.map(getVariable).toList(); | |
348 Statement catchBody = visit(node.handler.body); | |
349 return new Try(tryBody, catchParameters, catchBody); | |
350 } | |
351 | |
352 Statement visitInvokeStatic(cps_ir.InvokeStatic node) { | |
353 // Calls are translated to direct style. | |
354 List<Expression> arguments = translateArguments(node.arguments); | 369 List<Expression> arguments = translateArguments(node.arguments); |
355 Expression invoke = new InvokeStatic(node.target, node.selector, arguments, | 370 Expression invoke = new InvokeStatic(node.target, node.selector, arguments, |
356 node.sourceInformation); | 371 node.sourceInformation); |
357 return continueWithExpression(node.continuation, invoke); | 372 return makeCallExpression(node, invoke); |
358 } | 373 } |
359 | 374 |
360 Statement visitInvokeMethod(cps_ir.InvokeMethod node) { | 375 NodeCallback visitInvokeMethod(cps_ir.InvokeMethod node) { |
361 InvokeMethod invoke = new InvokeMethod( | 376 InvokeMethod invoke = new InvokeMethod( |
362 getVariableUse(node.receiver), | 377 getVariableUse(node.receiver), |
363 node.selector, | 378 node.selector, |
364 node.mask, | 379 node.mask, |
365 translateArguments(node.arguments), | 380 translateArguments(node.arguments), |
366 node.sourceInformation); | 381 node.sourceInformation); |
367 invoke.receiverIsNotNull = node.receiverIsNotNull; | 382 invoke.receiverIsNotNull = node.receiverIsNotNull; |
368 return continueWithExpression(node.continuation, invoke); | 383 return makeCallExpression(node, invoke); |
369 } | 384 } |
370 | 385 |
371 Statement visitInvokeMethodDirectly(cps_ir.InvokeMethodDirectly node) { | 386 NodeCallback visitInvokeMethodDirectly(cps_ir.InvokeMethodDirectly node) { |
372 Expression receiver = getVariableUse(node.receiver); | 387 Expression receiver = getVariableUse(node.receiver); |
373 List<Expression> arguments = translateArguments(node.arguments); | 388 List<Expression> arguments = translateArguments(node.arguments); |
374 Expression invoke = new InvokeMethodDirectly(receiver, node.target, | 389 Expression invoke = new InvokeMethodDirectly(receiver, node.target, |
375 node.selector, arguments, node.sourceInformation); | 390 node.selector, arguments, node.sourceInformation); |
376 return continueWithExpression(node.continuation, invoke); | 391 return makeCallExpression(node, invoke); |
377 } | 392 } |
378 | 393 |
| 394 NodeCallback visitTypeCast(cps_ir.TypeCast node) { |
| 395 Expression value = getVariableUse(node.value); |
| 396 List<Expression> typeArgs = translateArguments(node.typeArguments); |
| 397 Expression expression = |
| 398 new TypeOperator(value, node.type, typeArgs, isTypeTest: false); |
| 399 return makeCallExpression(node, expression); |
| 400 } |
| 401 |
| 402 NodeCallback visitInvokeConstructor(cps_ir.InvokeConstructor node) { |
| 403 List<Expression> arguments = translateArguments(node.arguments); |
| 404 Expression invoke = new InvokeConstructor( |
| 405 node.type, |
| 406 node.target, |
| 407 node.selector, |
| 408 arguments, |
| 409 node.sourceInformation); |
| 410 return makeCallExpression(node, invoke); |
| 411 } |
| 412 |
| 413 NodeCallback visitForeignCode(cps_ir.ForeignCode node) { |
| 414 if (node.codeTemplate.isExpression) { |
| 415 Expression foreignCode = new ForeignExpression( |
| 416 node.codeTemplate, |
| 417 node.type, |
| 418 node.arguments.map(getVariableUse).toList(growable: false), |
| 419 node.nativeBehavior, |
| 420 node.dependency); |
| 421 return makeCallExpression(node, foreignCode); |
| 422 } else { |
| 423 return (Statement next) { |
| 424 assert(next is Unreachable); // We are not using the `next` statement. |
| 425 return new ForeignStatement( |
| 426 node.codeTemplate, |
| 427 node.type, |
| 428 node.arguments.map(getVariableUse).toList(growable: false), |
| 429 node.nativeBehavior, |
| 430 node.dependency); |
| 431 }; |
| 432 } |
| 433 } |
| 434 |
| 435 NodeCallback visitGetLazyStatic(cps_ir.GetLazyStatic node) { |
| 436 // In the tree IR, GetStatic handles lazy fields because we do not need |
| 437 // as fine-grained control over side effects. |
| 438 GetStatic value = new GetStatic(node.element, node.sourceInformation); |
| 439 return makeCallExpression(node, value); |
| 440 } |
| 441 |
| 442 |
| 443 /************************** TAIL EXPRESSIONS **************************/ |
| 444 // |
| 445 // Visit methods for tail expressions must return a statement directly |
| 446 // (not a function like interior and call expressions). |
| 447 |
379 Statement visitThrow(cps_ir.Throw node) { | 448 Statement visitThrow(cps_ir.Throw node) { |
380 Expression value = getVariableUse(node.value); | 449 Expression value = getVariableUse(node.value); |
381 return new Throw(value); | 450 return new Throw(value); |
382 } | 451 } |
383 | 452 |
384 Statement visitRethrow(cps_ir.Rethrow node) { | 453 Statement visitRethrow(cps_ir.Rethrow node) { |
385 return new Rethrow(); | 454 return new Rethrow(); |
386 } | 455 } |
387 | 456 |
388 Statement visitUnreachable(cps_ir.Unreachable node) { | 457 Statement visitUnreachable(cps_ir.Unreachable node) { |
389 return new Unreachable(); | 458 return new Unreachable(); |
390 } | 459 } |
391 | 460 |
392 Statement continueWithExpression(cps_ir.Reference continuation, | |
393 Expression expression) { | |
394 cps_ir.Continuation cont = continuation.definition; | |
395 if (cont == returnContinuation) { | |
396 return new Return(expression); | |
397 } else { | |
398 assert(cont.parameters.length == 1); | |
399 Function nextBuilder = cont.hasExactlyOneUse ? | |
400 () => visit(cont.body) : () => new Break(labels[cont]); | |
401 return buildContinuationAssignment(cont.parameters.single, expression, | |
402 nextBuilder); | |
403 } | |
404 } | |
405 | |
406 Statement visitLetMutable(cps_ir.LetMutable node) { | |
407 Variable variable = addMutableVariable(node.variable); | |
408 Expression value = getVariableUse(node.value); | |
409 Statement body = visit(node.body); | |
410 return Assign.makeStatement(variable, value, body); | |
411 } | |
412 | |
413 Expression visitGetMutable(cps_ir.GetMutable node) { | |
414 return getMutableVariableUse(node.variable); | |
415 } | |
416 | |
417 Expression visitSetMutable(cps_ir.SetMutable node) { | |
418 Variable variable = getMutableVariable(node.variable.definition); | |
419 Expression value = getVariableUse(node.value); | |
420 return new Assign(variable, value); | |
421 } | |
422 | |
423 Statement visitTypeCast(cps_ir.TypeCast node) { | |
424 Expression value = getVariableUse(node.value); | |
425 List<Expression> typeArgs = translateArguments(node.typeArguments); | |
426 Expression expression = | |
427 new TypeOperator(value, node.type, typeArgs, isTypeTest: false); | |
428 return continueWithExpression(node.continuation, expression); | |
429 } | |
430 | |
431 Expression visitTypeTest(cps_ir.TypeTest node) { | |
432 Expression value = getVariableUse(node.value); | |
433 List<Expression> typeArgs = translateArguments(node.typeArguments); | |
434 return new TypeOperator(value, node.type, typeArgs, isTypeTest: true); | |
435 } | |
436 | |
437 Statement visitInvokeConstructor(cps_ir.InvokeConstructor node) { | |
438 List<Expression> arguments = translateArguments(node.arguments); | |
439 Expression invoke = new InvokeConstructor( | |
440 node.type, | |
441 node.target, | |
442 node.selector, | |
443 arguments, | |
444 node.sourceInformation); | |
445 return continueWithExpression(node.continuation, invoke); | |
446 } | |
447 | |
448 Statement visitInvokeContinuation(cps_ir.InvokeContinuation node) { | 461 Statement visitInvokeContinuation(cps_ir.InvokeContinuation node) { |
449 // Invocations of the return continuation are translated to returns. | 462 // Invocations of the return continuation are translated to returns. |
450 // Other continuation invocations are replaced with assignments of the | 463 // Other continuation invocations are replaced with assignments of the |
451 // arguments to formal parameter variables, followed by the body if | 464 // arguments to formal parameter variables, followed by the body if |
452 // the continuation is singly reference or a break if it is multiply | 465 // the continuation is singly reference or a break if it is multiply |
453 // referenced. | 466 // referenced. |
454 cps_ir.Continuation cont = node.continuation.definition; | 467 cps_ir.Continuation cont = node.continuation.definition; |
455 if (cont == returnContinuation) { | 468 if (cont == returnContinuation) { |
456 assert(node.arguments.length == 1); | 469 assert(node.arguments.length == 1); |
457 return new Return(getVariableUse(node.arguments.single), | 470 return new Return(getVariableUse(node.arguments.single), |
458 sourceInformation: node.sourceInformation); | 471 sourceInformation: node.sourceInformation); |
459 } else { | 472 } else { |
460 List<Expression> arguments = translateArguments(node.arguments); | 473 List<Expression> arguments = translateArguments(node.arguments); |
461 return buildPhiAssignments(cont.parameters, arguments, | 474 return buildPhiAssignments(cont.parameters, arguments, |
462 () { | 475 () { |
463 // Translate invocations of recursive and non-recursive | 476 // Translate invocations of recursive and non-recursive |
464 // continuations differently. | 477 // continuations differently. |
465 // * Non-recursive continuations | 478 // * Non-recursive continuations |
466 // - If there is one use, translate the continuation body | 479 // - If there is one use, translate the continuation body |
467 // inline at the invocation site. | 480 // inline at the invocation site. |
468 // - If there are multiple uses, translate to Break. | 481 // - If there are multiple uses, translate to Break. |
469 // * Recursive continuations | 482 // * Recursive continuations |
470 // - There is a single non-recursive invocation. Translate | 483 // - There is a single non-recursive invocation. Translate |
471 // the continuation body inline as a labeled loop at the | 484 // the continuation body inline as a labeled loop at the |
472 // invocation site. | 485 // invocation site. |
473 // - Translate the recursive invocations to Continue. | 486 // - Translate the recursive invocations to Continue. |
474 if (cont.isRecursive) { | 487 if (cont.isRecursive) { |
475 return node.isRecursive | 488 return node.isRecursive |
476 ? new Continue(labels[cont]) | 489 ? new Continue(getLabel(cont)) |
477 : new WhileTrue(labels[cont], visit(cont.body)); | 490 : new WhileTrue(getLabel(cont), |
| 491 translateExpression(cont.body)); |
478 } else { | 492 } else { |
479 if (cont.hasExactlyOneUse) { | 493 return cont.hasExactlyOneUse && !node.isEscapingTry |
480 if (!node.isEscapingTry) { | 494 ? translateExpression(cont.body) |
481 return visit(cont.body); | 495 : new Break(getLabel(cont)); |
482 } | |
483 labels[cont] = new Label(); | |
484 } | |
485 return new Break(labels[cont]); | |
486 } | 496 } |
487 }); | 497 }); |
488 } | 498 } |
489 } | 499 } |
490 | 500 |
491 Statement visitBranch(cps_ir.Branch node) { | 501 Statement visitBranch(cps_ir.Branch node) { |
492 Expression condition = visit(node.condition); | 502 Expression condition = translateCondition(node.condition); |
493 Statement thenStatement, elseStatement; | 503 Statement thenStatement, elseStatement; |
494 cps_ir.Continuation cont = node.trueContinuation.definition; | 504 cps_ir.Continuation cont = node.trueContinuation.definition; |
495 assert(cont.parameters.isEmpty); | 505 assert(cont.parameters.isEmpty); |
496 thenStatement = | 506 thenStatement = cont.hasExactlyOneUse |
497 cont.hasExactlyOneUse ? visit(cont.body) : new Break(labels[cont]); | 507 ? translateExpression(cont.body) |
| 508 : new Break(labels[cont]); |
498 cont = node.falseContinuation.definition; | 509 cont = node.falseContinuation.definition; |
499 assert(cont.parameters.isEmpty); | 510 assert(cont.parameters.isEmpty); |
500 elseStatement = | 511 elseStatement = cont.hasExactlyOneUse |
501 cont.hasExactlyOneUse ? visit(cont.body) : new Break(labels[cont]); | 512 ? translateExpression(cont.body) |
| 513 : new Break(labels[cont]); |
502 return new If(condition, thenStatement, elseStatement); | 514 return new If(condition, thenStatement, elseStatement); |
503 } | 515 } |
504 | 516 |
| 517 |
| 518 /************************** PRIMITIVES **************************/ |
| 519 // |
| 520 // Visit methods for primitives must return an expression. |
| 521 // |
| 522 |
| 523 Expression visitSetField(cps_ir.SetField node) { |
| 524 return new SetField(getVariableUse(node.object), |
| 525 node.field, |
| 526 getVariableUse(node.value)); |
| 527 } |
| 528 |
| 529 Expression visitInterceptor(cps_ir.Interceptor node) { |
| 530 return new Interceptor(getVariableUse(node.input), |
| 531 node.interceptedClasses, |
| 532 node.sourceInformation); |
| 533 } |
| 534 |
| 535 Expression visitCreateInstance(cps_ir.CreateInstance node) { |
| 536 return new CreateInstance( |
| 537 node.classElement, |
| 538 translateArguments(node.arguments), |
| 539 translateArguments(node.typeInformation), |
| 540 node.sourceInformation); |
| 541 } |
| 542 |
| 543 Expression visitGetField(cps_ir.GetField node) { |
| 544 return new GetField(getVariableUse(node.object), node.field, |
| 545 objectIsNotNull: node.objectIsNotNull); |
| 546 } |
| 547 |
| 548 Expression visitCreateBox(cps_ir.CreateBox node) { |
| 549 return new CreateBox(); |
| 550 } |
| 551 |
| 552 Expression visitCreateInvocationMirror(cps_ir.CreateInvocationMirror node) { |
| 553 return new CreateInvocationMirror( |
| 554 node.selector, |
| 555 translateArguments(node.arguments)); |
| 556 } |
| 557 |
| 558 Expression visitGetMutable(cps_ir.GetMutable node) { |
| 559 return getMutableVariableUse(node.variable); |
| 560 } |
| 561 |
| 562 Expression visitSetMutable(cps_ir.SetMutable node) { |
| 563 Variable variable = getMutableVariable(node.variable.definition); |
| 564 Expression value = getVariableUse(node.value); |
| 565 return new Assign(variable, value); |
| 566 } |
| 567 |
505 Expression visitConstant(cps_ir.Constant node) { | 568 Expression visitConstant(cps_ir.Constant node) { |
506 return new Constant(node.value, sourceInformation: node.sourceInformation); | 569 return new Constant(node.value, sourceInformation: node.sourceInformation); |
507 } | 570 } |
508 | 571 |
509 Expression visitLiteralList(cps_ir.LiteralList node) { | 572 Expression visitLiteralList(cps_ir.LiteralList node) { |
510 return new LiteralList( | 573 return new LiteralList( |
511 node.type, | 574 node.type, |
512 translateArguments(node.values)); | 575 translateArguments(node.values)); |
513 } | 576 } |
514 | 577 |
(...skipping 10 matching lines...) Expand all Loading... |
525 | 588 |
526 FunctionDefinition makeSubFunction(cps_ir.FunctionDefinition function) { | 589 FunctionDefinition makeSubFunction(cps_ir.FunctionDefinition function) { |
527 return createInnerBuilder().buildFunction(function); | 590 return createInnerBuilder().buildFunction(function); |
528 } | 591 } |
529 | 592 |
530 Expression visitCreateFunction(cps_ir.CreateFunction node) { | 593 Expression visitCreateFunction(cps_ir.CreateFunction node) { |
531 FunctionDefinition def = makeSubFunction(node.definition); | 594 FunctionDefinition def = makeSubFunction(node.definition); |
532 return new FunctionExpression(def); | 595 return new FunctionExpression(def); |
533 } | 596 } |
534 | 597 |
535 visitParameter(cps_ir.Parameter node) { | |
536 // Continuation parameters are not visited (continuations themselves are | |
537 // not visited yet). | |
538 unexpectedNode(node); | |
539 } | |
540 | |
541 visitContinuation(cps_ir.Continuation node) { | |
542 // Until continuations with multiple uses are supported, they are not | |
543 // visited. | |
544 unexpectedNode(node); | |
545 } | |
546 | |
547 visitMutableVariable(cps_ir.MutableVariable node) { | |
548 // These occur as parameters or bound by LetMutable. They are not visited | |
549 // directly. | |
550 unexpectedNode(node); | |
551 } | |
552 | |
553 Expression visitIsTrue(cps_ir.IsTrue node) { | |
554 return getVariableUse(node.value); | |
555 } | |
556 | |
557 Expression visitReifyRuntimeType(cps_ir.ReifyRuntimeType node) { | 598 Expression visitReifyRuntimeType(cps_ir.ReifyRuntimeType node) { |
558 return new ReifyRuntimeType( | 599 return new ReifyRuntimeType( |
559 getVariableUse(node.value), node.sourceInformation); | 600 getVariableUse(node.value), node.sourceInformation); |
560 } | 601 } |
561 | 602 |
562 Expression visitReadTypeVariable(cps_ir.ReadTypeVariable node) { | 603 Expression visitReadTypeVariable(cps_ir.ReadTypeVariable node) { |
563 return new ReadTypeVariable( | 604 return new ReadTypeVariable( |
564 node.variable, | 605 node.variable, |
565 getVariableUse(node.target), | 606 getVariableUse(node.target), |
566 node.sourceInformation); | 607 node.sourceInformation); |
567 } | 608 } |
568 | 609 |
569 @override | 610 Expression visitTypeExpression(cps_ir.TypeExpression node) { |
570 Node visitTypeExpression(cps_ir.TypeExpression node) { | |
571 return new TypeExpression( | 611 return new TypeExpression( |
572 node.dartType, | 612 node.dartType, |
573 node.arguments.map(getVariableUse).toList()); | 613 node.arguments.map(getVariableUse).toList()); |
574 } | 614 } |
575 | 615 |
| 616 Expression visitTypeTest(cps_ir.TypeTest node) { |
| 617 Expression value = getVariableUse(node.value); |
| 618 List<Expression> typeArgs = translateArguments(node.typeArguments); |
| 619 return new TypeOperator(value, node.type, typeArgs, isTypeTest: true); |
| 620 } |
| 621 |
576 Expression visitGetStatic(cps_ir.GetStatic node) { | 622 Expression visitGetStatic(cps_ir.GetStatic node) { |
577 return new GetStatic(node.element, node.sourceInformation); | 623 return new GetStatic(node.element, node.sourceInformation); |
578 } | 624 } |
579 | 625 |
580 Statement visitGetLazyStatic(cps_ir.GetLazyStatic node) { | |
581 // In the tree IR, GetStatic handles lazy fields because tree | |
582 // expressions are allowed to have side effects. | |
583 GetStatic value = new GetStatic(node.element, node.sourceInformation); | |
584 return continueWithExpression(node.continuation, value); | |
585 } | |
586 | |
587 Expression visitSetStatic(cps_ir.SetStatic node) { | 626 Expression visitSetStatic(cps_ir.SetStatic node) { |
588 return new SetStatic( | 627 return new SetStatic( |
589 node.element, | 628 node.element, |
590 getVariableUse(node.value), | 629 getVariableUse(node.value), |
591 node.sourceInformation); | 630 node.sourceInformation); |
592 } | 631 } |
593 | 632 |
594 Expression visitApplyBuiltinOperator(cps_ir.ApplyBuiltinOperator node) { | 633 Expression visitApplyBuiltinOperator(cps_ir.ApplyBuiltinOperator node) { |
595 if (node.operator == BuiltinOperator.IsFalsy) { | 634 if (node.operator == BuiltinOperator.IsFalsy) { |
596 return new Not(getVariableUse(node.arguments.single)); | 635 return new Not(getVariableUse(node.arguments.single)); |
597 } | 636 } |
598 return new ApplyBuiltinOperator(node.operator, | 637 return new ApplyBuiltinOperator(node.operator, |
599 translateArguments(node.arguments)); | 638 translateArguments(node.arguments)); |
600 } | 639 } |
601 | 640 |
602 Statement visitForeignCode(cps_ir.ForeignCode node) { | |
603 if (node.codeTemplate.isExpression) { | |
604 Expression foreignCode = new ForeignExpression( | |
605 node.codeTemplate, | |
606 node.type, | |
607 node.arguments.map(getVariableUse).toList(growable: false), | |
608 node.nativeBehavior, | |
609 node.dependency); | |
610 return continueWithExpression(node.continuation, foreignCode); | |
611 } else { | |
612 assert(node.continuation.definition.body is cps_ir.Unreachable); | |
613 return new ForeignStatement( | |
614 node.codeTemplate, | |
615 node.type, | |
616 node.arguments.map(getVariableUse).toList(growable: false), | |
617 node.nativeBehavior, | |
618 node.dependency); | |
619 } | |
620 } | |
621 | |
622 Expression visitGetLength(cps_ir.GetLength node) { | 641 Expression visitGetLength(cps_ir.GetLength node) { |
623 return new GetLength(getVariableUse(node.object)); | 642 return new GetLength(getVariableUse(node.object)); |
624 } | 643 } |
625 | 644 |
626 Expression visitGetIndex(cps_ir.GetIndex node) { | 645 Expression visitGetIndex(cps_ir.GetIndex node) { |
627 return new GetIndex(getVariableUse(node.object), | 646 return new GetIndex(getVariableUse(node.object), |
628 getVariableUse(node.index)); | 647 getVariableUse(node.index)); |
629 } | 648 } |
630 | 649 |
631 Expression visitSetIndex(cps_ir.SetIndex node) { | 650 Expression visitSetIndex(cps_ir.SetIndex node) { |
632 return new SetIndex(getVariableUse(node.object), | 651 return new SetIndex(getVariableUse(node.object), |
633 getVariableUse(node.index), | 652 getVariableUse(node.index), |
634 getVariableUse(node.value)); | 653 getVariableUse(node.value)); |
635 } | 654 } |
| 655 |
| 656 /********** UNUSED VISIT METHODS *************/ |
| 657 |
| 658 unexpectedNode(cps_ir.Node node) { |
| 659 internalError(CURRENT_ELEMENT_SPANNABLE, 'Unexpected IR node: $node'); |
| 660 } |
| 661 |
| 662 visitFunctionDefinition(cps_ir.FunctionDefinition node) { |
| 663 unexpectedNode(node); |
| 664 } |
| 665 visitParameter(cps_ir.Parameter node) => unexpectedNode(node); |
| 666 visitContinuation(cps_ir.Continuation node) => unexpectedNode(node); |
| 667 visitMutableVariable(cps_ir.MutableVariable node) => unexpectedNode(node); |
| 668 visitIsTrue(cps_ir.IsTrue node) => unexpectedNode(node); |
636 } | 669 } |
637 | 670 |
OLD | NEW |