Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(729)

Side by Side Diff: sdk/lib/_internal/compiler/implementation/cps_ir/cps_ir_builder.dart

Issue 694353007: Move dart2js from sdk/lib/_internal/compiler to pkg/compiler (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, 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 dart2js.ir_builder;
6
7 import '../constants/expressions.dart';
8 import '../constants/values.dart' show PrimitiveConstantValue;
9 import '../dart_backend/dart_backend.dart' show DartBackend;
10 import '../dart_types.dart';
11 import '../dart2jslib.dart';
12 import '../elements/elements.dart';
13 import '../source_file.dart';
14 import '../tree/tree.dart' as ast;
15 import '../scanner/scannerlib.dart' show Token, isUserDefinableOperator;
16 import '../universe/universe.dart' show SelectorKind;
17 import 'cps_ir_nodes.dart' as ir;
18
19 part 'cps_ir_builder_visitor.dart';
20
21 /// A mapping from variable elements to their compile-time values.
22 ///
23 /// Map elements denoted by parameters and local variables to the
24 /// [ir.Primitive] that is their value. Parameters and locals are
25 /// assigned indexes which can be used to refer to them.
26 class Environment {
27 /// A map from elements to their environment index.
28 final Map<Element, int> variable2index;
29
30 /// A reverse map from environment indexes to the variable.
31 final List<Element> index2variable;
32
33 /// A map from environment indexes to their value.
34 final List<ir.Primitive> index2value;
35
36 Environment.empty()
37 : variable2index = <Element, int>{},
38 index2variable = <Element>[],
39 index2value = <ir.Primitive>[];
40
41 /// Construct an environment that is a copy of another one.
42 ///
43 /// The mapping from elements to indexes is shared, not copied.
44 Environment.from(Environment other)
45 : variable2index = other.variable2index,
46 index2variable = new List<Element>.from(other.index2variable),
47 index2value = new List<ir.Primitive>.from(other.index2value);
48
49 get length => index2variable.length;
50
51 ir.Primitive operator [](int index) => index2value[index];
52
53 void extend(Element element, ir.Primitive value) {
54 // Assert that the name is not already in the environment. `null` is used
55 // as the name of anonymous variables. Because the variable2index map is
56 // shared, `null` can already occur. This is safe because such variables
57 // are not looked up by name.
58 //
59 // TODO(kmillikin): This is still kind of fishy. Refactor to not share
60 // name maps or else garbage collect unneeded names.
61 assert(element == null || !variable2index.containsKey(element));
62 variable2index[element] = index2variable.length;
63 index2variable.add(element);
64 index2value.add(value);
65 }
66
67 ir.Primitive lookup(Element element) {
68 assert(!element.isConst);
69 assert(invariant(element, variable2index.containsKey(element),
70 message: "Unknown variable: $element."));
71 return index2value[variable2index[element]];
72 }
73
74 void update(Element element, ir.Primitive value) {
75 index2value[variable2index[element]] = value;
76 }
77
78 /// Verify that the variable2index and index2variable maps agree up to the
79 /// index [length] exclusive.
80 bool sameDomain(int length, Environment other) {
81 assert(this.length >= length);
82 assert(other.length >= length);
83 for (int i = 0; i < length; ++i) {
84 // An index maps to the same variable in both environments.
85 Element variable = index2variable[i];
86 if (variable != other.index2variable[i]) return false;
87
88 // The variable maps to the same index in both environments.
89 int index = variable2index[variable];
90 if (index == null || index != other.variable2index[variable]) {
91 return false;
92 }
93 }
94 return true;
95 }
96 }
97
98 /// A class to collect breaks or continues.
99 ///
100 /// When visiting a potential target of breaks or continues, any breaks or
101 /// continues are collected by a JumpCollector and processed later, on demand.
102 /// The site of the break or continue is represented by a continuation
103 /// invocation that will have its target and arguments filled in later.
104 ///
105 /// The environment of the builder at that point is captured and should not
106 /// be subsequently mutated until the jump is resolved.
107 class JumpCollector {
108 final JumpTarget target;
109 final List<ir.InvokeContinuation> _invocations = <ir.InvokeContinuation>[];
110 final List<Environment> _environments = <Environment>[];
111
112 JumpCollector(this.target);
113
114 bool get isEmpty => _invocations.isEmpty;
115 int get length => _invocations.length;
116 List<ir.InvokeContinuation> get invocations => _invocations;
117 List<Environment> get environments => _environments;
118
119 void addJump(IrBuilder builder) {
120 ir.InvokeContinuation invoke = new ir.InvokeContinuation.uninitialized();
121 builder.add(invoke);
122 _invocations.add(invoke);
123 _environments.add(builder.environment);
124 builder._current = null;
125 // TODO(kmillikin): Can we set builder.environment to null to make it
126 // less likely to mutate it?
127 }
128 }
129
130 /// Function for building a node in the context of the current builder.
131 typedef ir.Node BuildFunction(node);
132
133 /// Function for building nodes in the context of the provided [builder].
134 typedef ir.Node SubbuildFunction(IrBuilder builder);
135
136 /// Mixin that provides encapsulated access to nested builders.
137 abstract class IrBuilderMixin<N> {
138 IrBuilder _irBuilder;
139
140 /// Execute [f] with [builder] as the current builder.
141 withBuilder(IrBuilder builder, f()) {
142 assert(builder != null);
143 IrBuilder prev = _irBuilder;
144 _irBuilder = builder;
145 var result = f();
146 _irBuilder = prev;
147 return result;
148 }
149
150 /// The current builder.
151 IrBuilder get irBuilder {
152 assert(_irBuilder != null);
153 return _irBuilder;
154 }
155
156 /// Visits the [node].
157 ir.Primitive visit(N node);
158
159 /// Builds and returns the [ir.Node] for [node] or returns `null` if
160 /// [node] is `null`.
161 ir.Node build(N node) => node != null ? visit(node) : null;
162
163 /// Returns a closure that takes an [IrBuilder] and builds [node] in its
164 /// context using [build].
165 SubbuildFunction subbuild(N node) {
166 return (IrBuilder builder) => withBuilder(builder, () => build(node));
167 }
168
169 /// Returns a closure that takes an [IrBuilder] and builds the sequence of
170 /// [nodes] in its context using [build].
171 // TODO(johnniwinther): Type [nodes] as `Iterable<N>` when `NodeList` uses
172 // `List` instead of `Link`.
173 SubbuildFunction subbuildSequence(/*Iterable<N>*/ nodes) {
174 return (IrBuilder builder) {
175 return withBuilder(builder, () => builder.buildSequence(nodes, build));
176 };
177 }
178 }
179
180 /// Shared state between nested builders.
181 class IrBuilderSharedState {
182 final ConstantSystem constantSystem;
183
184 /// A stack of collectors for breaks.
185 final List<JumpCollector> breakCollectors = <JumpCollector>[];
186
187 /// A stack of collectors for continues.
188 final List<JumpCollector> continueCollectors = <JumpCollector>[];
189
190 final List<ConstDeclaration> localConstants = <ConstDeclaration>[];
191
192 final Iterable<Entity> closureLocals;
193
194 final FunctionElement currentFunction;
195
196 final ir.Continuation returnContinuation = new ir.Continuation.retrn();
197
198 IrBuilderSharedState(this.constantSystem,
199 this.currentFunction,
200 this.closureLocals);
201 }
202
203 /// A factory for building the cps IR.
204 class IrBuilder {
205 // TODO(johnniwinther): Make these field final and remove the default values
206 // when [IrBuilder] is a property of [IrBuilderVisitor] instead of a mixin.
207
208 final List<ir.Parameter> _parameters = <ir.Parameter>[];
209
210 final IrBuilderSharedState state;
211
212 /// A map from variable indexes to their values.
213 Environment environment;
214
215 // The IR builder maintains a context, which is an expression with a hole in
216 // it. The hole represents the focus where new expressions can be added.
217 // The context is implemented by 'root' which is the root of the expression
218 // and 'current' which is the expression that immediately contains the hole.
219 // Not all expressions have a hole (e.g., invocations, which always occur in
220 // tail position, do not have a hole). Expressions with a hole have a plug
221 // method.
222 //
223 // Conceptually, visiting a statement takes a context as input and returns
224 // either a new context or else an expression without a hole if all
225 // control-flow paths through the statement have exited. An expression
226 // without a hole is represented by a (root, current) pair where root is the
227 // expression and current is null.
228 //
229 // Conceptually again, visiting an expression takes a context as input and
230 // returns either a pair of a new context and a definition denoting
231 // the expression's value, or else an expression without a hole if all
232 // control-flow paths through the expression have exited.
233 //
234 // We do not pass contexts as arguments or return them. Rather we use the
235 // current context (root, current) as the visitor state and mutate current.
236 // Visiting a statement returns null; visiting an expression returns the
237 // primitive denoting its value.
238
239 ir.Expression _root = null;
240 ir.Expression _current = null;
241
242 IrBuilder(ConstantSystem constantSystem,
243 FunctionElement currentFunction,
244 Iterable<Entity> closureLocals)
245 : this.state = new IrBuilderSharedState(
246 constantSystem, currentFunction, closureLocals),
247 this.environment = new Environment.empty();
248
249 /// Construct a delimited visitor for visiting a subtree.
250 ///
251 /// The delimited visitor has its own compile-time environment mapping
252 /// local variables to their values, which is initially a copy of the parent
253 /// environment. It has its own context for building an IR expression, so
254 /// the built expression is not plugged into the parent's context.
255 IrBuilder.delimited(IrBuilder parent)
256 : this.state = parent.state,
257 this.environment = new Environment.from(parent.environment);
258
259 /// Construct a visitor for a recursive continuation.
260 ///
261 /// The recursive continuation builder has fresh parameters (i.e. SSA phis)
262 /// for all the local variables in the parent, because the invocation sites
263 /// of the continuation are not all known when the builder is created. The
264 /// recursive invocations will be passed values for all the local variables,
265 /// which may be eliminated later if they are redundant---if they take on
266 /// the same value at all invocation sites.
267 IrBuilder.recursive(IrBuilder parent)
268 : this.state = parent.state,
269 this.environment = new Environment.empty() {
270 parent.environment.index2variable.forEach(createParameter);
271 }
272
273
274 bool get isOpen => _root == null || _current != null;
275
276 /// True if [element] is a local variable, local function, or parameter that
277 /// is accessed from an inner function. Recursive self-references in a local
278 /// function count as closure accesses.
279 ///
280 /// If `true`, [element] is a [LocalElement].
281 bool isClosureVariable(Element element) {
282 return state.closureLocals.contains(element);
283 }
284
285 /// Create a parameter for [parameterElement] and add it to the current
286 /// environment.
287 ///
288 /// [isClosureVariable] marks whether [parameterElement] is accessed from an
289 /// inner function.
290 void createParameter(LocalElement parameterElement) {
291 ir.Parameter parameter = new ir.Parameter(parameterElement);
292 _parameters.add(parameter);
293 if (isClosureVariable(parameterElement)) {
294 add(new ir.SetClosureVariable(parameterElement, parameter));
295 } else {
296 environment.extend(parameterElement, parameter);
297 }
298 }
299
300 /// Add the constant [variableElement] to the environment with [value] as its
301 /// constant value.
302 void declareLocalConstant(LocalVariableElement variableElement,
303 ConstantExpression value) {
304 state.localConstants.add(new ConstDeclaration(variableElement, value));
305 }
306
307 /// Add [variableElement] to the environment with [initialValue] as its
308 /// initial value.
309 ///
310 /// [isClosureVariable] marks whether [variableElement] is accessed from an
311 /// inner function.
312 void declareLocalVariable(LocalVariableElement variableElement,
313 {ir.Primitive initialValue}) {
314 assert(isOpen);
315 if (initialValue == null) {
316 // TODO(kmillikin): Consider pooling constants.
317 // The initial value is null.
318 initialValue = buildNullLiteral();
319 }
320 if (isClosureVariable(variableElement)) {
321 add(new ir.SetClosureVariable(variableElement,
322 initialValue,
323 isDeclaration: true));
324 } else {
325 // In case a primitive was introduced for the initializer expression,
326 // use this variable element to help derive a good name for it.
327 initialValue.useElementAsHint(variableElement);
328 environment.extend(variableElement, initialValue);
329 }
330 }
331
332 // Plug an expression into the 'hole' in the context being accumulated. The
333 // empty context (just a hole) is represented by root (and current) being
334 // null. Since the hole in the current context is filled by this function,
335 // the new hole must be in the newly added expression---which becomes the
336 // new value of current.
337 void add(ir.Expression expr) {
338 assert(isOpen);
339 if (_root == null) {
340 _root = _current = expr;
341 } else {
342 _current = _current.plug(expr);
343 }
344 }
345
346 ir.Primitive _continueWithExpression(ir.Expression build(ir.Continuation k)) {
347 ir.Parameter v = new ir.Parameter(null);
348 ir.Continuation k = new ir.Continuation([v]);
349 ir.Expression expression = build(k);
350 add(new ir.LetCont(k, expression));
351 return v;
352 }
353
354 ir.Primitive _buildInvokeStatic(Element element,
355 Selector selector,
356 List<ir.Definition> arguments) {
357 assert(isOpen);
358 return _continueWithExpression(
359 (k) => new ir.InvokeStatic(element, selector, k, arguments));
360 }
361
362 ir.Primitive _buildInvokeSuper(Selector selector,
363 List<ir.Definition> arguments) {
364 assert(isOpen);
365 return _continueWithExpression(
366 (k) => new ir.InvokeSuperMethod(selector, k, arguments));
367 }
368
369 ir.Primitive _buildInvokeDynamic(ir.Primitive receiver,
370 Selector selector,
371 List<ir.Definition> arguments) {
372 assert(isOpen);
373 return _continueWithExpression(
374 (k) => new ir.InvokeMethod(receiver, selector, k, arguments));
375 }
376
377
378 /// Create a constant literal from [constant].
379 ir.Constant buildConstantLiteral(ConstantExpression constant) {
380 assert(isOpen);
381 ir.Constant prim = new ir.Constant(constant);
382 add(new ir.LetPrim(prim));
383 return prim;
384 }
385
386 // Helper for building primitive literals.
387 ir.Constant _buildPrimitiveConstant(PrimitiveConstantValue constant) {
388 return buildConstantLiteral(new PrimitiveConstantExpression(constant));
389 }
390
391 /// Create an integer literal.
392 ir.Constant buildIntegerLiteral(int value) {
393 return _buildPrimitiveConstant(state.constantSystem.createInt(value));
394 }
395
396 /// Create an double literal.
397 ir.Constant buildDoubleLiteral(double value) {
398 return _buildPrimitiveConstant(state.constantSystem.createDouble(value));
399 }
400
401 /// Create an bool literal.
402 ir.Constant buildBooleanLiteral(bool value) {
403 return _buildPrimitiveConstant(state.constantSystem.createBool(value));
404 }
405
406 /// Create an null literal.
407 ir.Constant buildNullLiteral() {
408 return _buildPrimitiveConstant(state.constantSystem.createNull());
409 }
410
411 /// Create a string literal.
412 ir.Constant buildStringLiteral(String value) {
413 return _buildPrimitiveConstant(
414 state.constantSystem.createString(new ast.DartString.literal(value)));
415 }
416
417 /// Creates a non-constant list literal of the provided [type] and with the
418 /// provided [values].
419 ir.Primitive buildListLiteral(InterfaceType type,
420 Iterable<ir.Primitive> values) {
421 assert(isOpen);
422 ir.Primitive result = new ir.LiteralList(type, values);
423 add(new ir.LetPrim(result));
424 return result;
425 }
426
427 /// Creates a non-constant map literal of the provided [type] and with the
428 /// entries build from the [keys] and [values] using [build].
429 ir.Primitive buildMapLiteral(InterfaceType type,
430 Iterable keys,
431 Iterable values,
432 BuildFunction build) {
433 assert(isOpen);
434 List<ir.LiteralMapEntry> entries = <ir.LiteralMapEntry>[];
435 Iterator key = keys.iterator;
436 Iterator value = values.iterator;
437 while (key.moveNext() && value.moveNext()) {
438 entries.add(new ir.LiteralMapEntry(
439 build(key.current), build(value.current)));
440 }
441 assert(!key.moveNext() && !value.moveNext());
442 ir.Primitive result = new ir.LiteralMap(type, entries);
443 add(new ir.LetPrim(result));
444 return result;
445 }
446
447 /// Creates a conditional expression with the provided [condition] where the
448 /// then and else expression are created through the [buildThenExpression] and
449 /// [buildElseExpression] functions, respectively.
450 ir.Primitive buildConditional(
451 ir.Primitive condition,
452 ir.Primitive buildThenExpression(IrBuilder builder),
453 ir.Primitive buildElseExpression(IrBuilder builder)) {
454
455 assert(isOpen);
456
457 // The then and else expressions are delimited.
458 IrBuilder thenBuilder = new IrBuilder.delimited(this);
459 IrBuilder elseBuilder = new IrBuilder.delimited(this);
460 ir.Primitive thenValue = buildThenExpression(thenBuilder);
461 ir.Primitive elseValue = buildElseExpression(elseBuilder);
462
463 // Treat the values of the subexpressions as named values in the
464 // environment, so they will be treated as arguments to the join-point
465 // continuation.
466 assert(environment.length == thenBuilder.environment.length);
467 assert(environment.length == elseBuilder.environment.length);
468 thenBuilder.environment.extend(null, thenValue);
469 elseBuilder.environment.extend(null, elseValue);
470 JumpCollector jumps = new JumpCollector(null);
471 jumps.addJump(thenBuilder);
472 jumps.addJump(elseBuilder);
473 ir.Continuation joinContinuation =
474 createJoin(environment.length + 1, jumps);
475
476 // Build the term
477 // let cont join(x, ..., result) = [] in
478 // let cont then() = [[thenPart]]; join(v, ...) in
479 // let cont else() = [[elsePart]]; join(v, ...) in
480 // if condition (then, else)
481 ir.Continuation thenContinuation = new ir.Continuation([]);
482 ir.Continuation elseContinuation = new ir.Continuation([]);
483 thenContinuation.body = thenBuilder._root;
484 elseContinuation.body = elseBuilder._root;
485 add(new ir.LetCont(joinContinuation,
486 new ir.LetCont(thenContinuation,
487 new ir.LetCont(elseContinuation,
488 new ir.Branch(new ir.IsTrue(condition),
489 thenContinuation,
490 elseContinuation)))));
491 return (thenValue == elseValue)
492 ? thenValue
493 : joinContinuation.parameters.last;
494
495 }
496
497 /**
498 * Add an explicit `return null` for functions that don't have a return
499 * statement on each branch. This includes functions with an empty body,
500 * such as `foo(){ }`.
501 */
502 void _ensureReturn() {
503 if (!isOpen) return;
504 ir.Constant constant = buildNullLiteral();
505 add(new ir.InvokeContinuation(state.returnContinuation, [constant]));
506 _current = null;
507 }
508
509 /// Create a [ir.FunctionDefinition] for [element] using [_root] as the body.
510 ///
511 /// Parameters must be created before the construction of the body using
512 /// [createParameter].
513 ir.FunctionDefinition buildFunctionDefinition(
514 FunctionElement element,
515 List<ConstantExpression> defaults) {
516 if (!element.isAbstract) {
517 _ensureReturn();
518 return new ir.FunctionDefinition(
519 element, state.returnContinuation, _parameters, _root,
520 state.localConstants, defaults);
521 } else {
522 assert(invariant(element, _root == null,
523 message: "Non-empty body for abstract method $element: $_root"));
524 assert(invariant(element, state.localConstants.isEmpty,
525 message: "Local constants for abstract method $element: "
526 "${state.localConstants}"));
527 return new ir.FunctionDefinition.abstract(
528 element, _parameters, defaults);
529 }
530 }
531
532
533 /// Create a super invocation where the method name and the argument structure
534 /// are defined by [selector] and the argument values are defined by
535 /// [arguments].
536 ir.Primitive buildSuperInvocation(Selector selector,
537 List<ir.Definition> arguments) {
538 return _buildInvokeSuper(selector, arguments);
539 }
540
541 /// Create a getter invocation on the super class where the getter name is
542 /// defined by [selector].
543 ir.Primitive buildSuperGet(Selector selector) {
544 assert(selector.isGetter);
545 return _buildInvokeSuper(selector, const <ir.Definition>[]);
546 }
547
548 /// Create a setter invocation on the super class where the setter name and
549 /// argument are defined by [selector] and [value], respectively.
550 ir.Primitive buildSuperSet(Selector selector, ir.Primitive value) {
551 assert(selector.isSetter);
552 _buildInvokeSuper(selector, <ir.Definition>[value]);
553 return value;
554 }
555
556 /// Create an index set invocation on the super class with the provided
557 /// [index] and [value].
558 ir.Primitive buildSuperIndexSet(ir.Primitive index,
559 ir.Primitive value) {
560 _buildInvokeSuper(new Selector.indexSet(), <ir.Definition>[index, value]);
561 return value;
562 }
563
564 /// Create a dynamic invocation on [receiver] where the method name and
565 /// argument structure are defined by [selector] and the argument values are
566 /// defined by [arguments].
567 ir.Primitive buildDynamicInvocation(ir.Definition receiver,
568 Selector selector,
569 List<ir.Definition> arguments) {
570 return _buildInvokeDynamic(receiver, selector, arguments);
571 }
572
573 /// Create a dynamic getter invocation on [receiver] where the getter name is
574 /// defined by [selector].
575 ir.Primitive buildDynamicGet(ir.Primitive receiver, Selector selector) {
576 assert(selector.isGetter);
577 return _buildInvokeDynamic(receiver, selector, const <ir.Definition>[]);
578 }
579
580 /// Create a dynamic setter invocation on [receiver] where the setter name and
581 /// argument are defined by [selector] and [value], respectively.
582 ir.Primitive buildDynamicSet(ir.Primitive receiver,
583 Selector selector,
584 ir.Primitive value) {
585 assert(selector.isSetter);
586 _buildInvokeDynamic(receiver, selector, <ir.Definition>[value]);
587 return value;
588 }
589
590 /// Create a dynamic index set invocation on [receiver] with the provided
591 /// [index] and [value].
592 ir.Primitive buildDynamicIndexSet(ir.Primitive receiver,
593 ir.Primitive index,
594 ir.Primitive value) {
595 _buildInvokeDynamic(
596 receiver, new Selector.indexSet(), <ir.Definition>[index, value]);
597 return value;
598 }
599
600 /// Create a static invocation of [element] where argument structure is
601 /// defined by [selector] and the argument values are defined by [arguments].
602 ir.Primitive buildStaticInvocation(Element element,
603 Selector selector,
604 List<ir.Definition> arguments) {
605 return _buildInvokeStatic(element, selector, arguments);
606 }
607
608 /// Create a static getter invocation of [element] where the getter name is
609 /// defined by [selector].
610 ir.Primitive buildStaticGet(Element element, Selector selector) {
611 assert(selector.isGetter);
612 return _buildInvokeStatic(element, selector, const <ir.Definition>[]);
613 }
614
615 /// Create a static setter invocation of [element] where the setter name and
616 /// argument are defined by [selector] and [value], respectively.
617 ir.Primitive buildStaticSet(Element element,
618 Selector selector,
619 ir.Primitive value) {
620 assert(selector.isSetter);
621 _buildInvokeStatic(element, selector, <ir.Definition>[value]);
622 return value;
623 }
624
625 /// Create a constructor invocation of [element] on [type] where the
626 /// constructor name and argument structure are defined by [selector] and the
627 /// argument values are defined by [arguments].
628 ir.Primitive buildConstructorInvocation(FunctionElement element,
629 Selector selector,
630 DartType type,
631 List<ir.Definition> arguments) {
632 assert(isOpen);
633 return _continueWithExpression(
634 (k) => new ir.InvokeConstructor(type, element, selector, k, arguments));
635 }
636
637 /// Create a string concatenation of the [arguments].
638 ir.Primitive buildStringConcatenation(List<ir.Definition> arguments) {
639 assert(isOpen);
640 return _continueWithExpression(
641 (k) => new ir.ConcatenateStrings(k, arguments));
642 }
643
644 /// Create a read access of [local].
645 ir.Primitive buildLocalGet(LocalElement local) {
646 assert(isOpen);
647 if (isClosureVariable(local)) {
648 ir.Primitive result = new ir.GetClosureVariable(local);
649 add(new ir.LetPrim(result));
650 return result;
651 } else {
652 return environment.lookup(local);
653 }
654 }
655
656 /// Create a write access to [local] with the provided [value].
657 ir.Primitive buildLocalSet(LocalElement local, ir.Primitive value) {
658 assert(isOpen);
659 if (isClosureVariable(local)) {
660 add(new ir.SetClosureVariable(local, value));
661 } else {
662 value.useElementAsHint(local);
663 environment.update(local, value);
664 }
665 return value;
666 }
667
668 /// Creates an if-then-else statement with the provided [condition] where the
669 /// then and else branches are created through the [buildThenPart] and
670 /// [buildElsePart] functions, respectively.
671 ///
672 /// An if-then statement is created if [buildElsePart] is a no-op.
673 // TODO(johnniwinther): Unify implementation with [buildConditional] and
674 // [_buildLogicalOperator].
675 void buildIf(ir.Primitive condition,
676 void buildThenPart(IrBuilder builder),
677 void buildElsePart(IrBuilder builder)) {
678 assert(isOpen);
679
680 // The then and else parts are delimited.
681 IrBuilder thenBuilder = new IrBuilder.delimited(this);
682 IrBuilder elseBuilder = new IrBuilder.delimited(this);
683 buildThenPart(thenBuilder);
684 buildElsePart(elseBuilder);
685
686 // Build the term
687 // (Result =) let cont then() = [[thenPart]] in
688 // let cont else() = [[elsePart]] in
689 // if condition (then, else)
690 ir.Continuation thenContinuation = new ir.Continuation([]);
691 ir.Continuation elseContinuation = new ir.Continuation([]);
692 ir.Expression letElse =
693 new ir.LetCont(elseContinuation,
694 new ir.Branch(new ir.IsTrue(condition),
695 thenContinuation,
696 elseContinuation));
697 ir.Expression letThen = new ir.LetCont(thenContinuation, letElse);
698 ir.Expression result = letThen;
699
700 ir.Continuation joinContinuation; // Null if there is no join.
701 if (thenBuilder.isOpen && elseBuilder.isOpen) {
702 // There is a join-point continuation. Build the term
703 // 'let cont join(x, ...) = [] in Result' and plug invocations of the
704 // join-point continuation into the then and else continuations.
705 JumpCollector jumps = new JumpCollector(null);
706 jumps.addJump(thenBuilder);
707 jumps.addJump(elseBuilder);
708 joinContinuation = createJoin(environment.length, jumps);
709 result = new ir.LetCont(joinContinuation, result);
710 }
711
712 // The then or else term root could be null, but not both. If there is
713 // a join then an InvokeContinuation was just added to both of them. If
714 // there is no join, then at least one of them is closed and thus has a
715 // non-null root by the definition of the predicate isClosed. In the
716 // case that one of them is null, it must be the only one that is open
717 // and thus contains the new hole in the context. This case is handled
718 // after the branch is plugged into the current hole.
719 thenContinuation.body = thenBuilder._root;
720 elseContinuation.body = elseBuilder._root;
721
722 add(result);
723 if (joinContinuation == null) {
724 // At least one subexpression is closed.
725 if (thenBuilder.isOpen) {
726 _current =
727 (thenBuilder._root == null) ? letThen : thenBuilder._current;
728 environment = thenBuilder.environment;
729 } else if (elseBuilder.isOpen) {
730 _current =
731 (elseBuilder._root == null) ? letElse : elseBuilder._current;
732 environment = elseBuilder.environment;
733 } else {
734 _current = null;
735 }
736 }
737 }
738
739 /// Invoke a join-point continuation that contains arguments for all local
740 /// variables.
741 ///
742 /// Given the continuation and a list of uninitialized invocations, fill
743 /// in each invocation with the continuation and appropriate arguments.
744 void invokeFullJoin(ir.Continuation join,
745 JumpCollector jumps,
746 {recursive: false}) {
747 join.isRecursive = recursive;
748 for (int i = 0; i < jumps.length; ++i) {
749 Environment currentEnvironment = jumps.environments[i];
750 ir.InvokeContinuation invoke = jumps.invocations[i];
751 invoke.continuation = new ir.Reference(join);
752 invoke.arguments = new List<ir.Reference>.generate(
753 join.parameters.length,
754 (i) => new ir.Reference(currentEnvironment[i]));
755 invoke.isRecursive = recursive;
756 }
757 }
758
759 /// Creates a for loop in which the initializer, condition, body, update are
760 /// created by [buildInitializer], [buildCondition], [buildBody] and
761 /// [buildUpdate], respectively.
762 ///
763 /// The jump [target] is used to identify which `break` and `continue`
764 /// statements that have this `for` statement as their target.
765 void buildFor({SubbuildFunction buildInitializer,
766 SubbuildFunction buildCondition,
767 SubbuildFunction buildBody,
768 SubbuildFunction buildUpdate,
769 JumpTarget target}) {
770 assert(isOpen);
771
772 // For loops use four named continuations: the entry to the condition,
773 // the entry to the body, the loop exit, and the loop successor (break).
774 // The CPS translation of
775 // [[for (initializer; condition; update) body; successor]] is:
776 //
777 // [[initializer]];
778 // let cont loop(x, ...) =
779 // let prim cond = [[condition]] in
780 // let cont break() = [[successor]] in
781 // let cont exit() = break(v, ...) in
782 // let cont body() =
783 // let cont continue(x, ...) = [[update]]; loop(v, ...) in
784 // [[body]]; continue(v, ...) in
785 // branch cond (body, exit) in
786 // loop(v, ...)
787 //
788 // If there are no breaks in the body, the break continuation is inlined
789 // in the exit continuation (i.e., the translation of the successor
790 // statement occurs in the exit continuation). If there is only one
791 // invocation of the continue continuation (i.e., no continues in the
792 // body), the continue continuation is inlined in the body.
793
794 buildInitializer(this);
795
796 IrBuilder condBuilder = new IrBuilder.recursive(this);
797 ir.Primitive condition = buildCondition(condBuilder);
798 if (condition == null) {
799 // If the condition is empty then the body is entered unconditionally.
800 condition = condBuilder.buildBooleanLiteral(true);
801 }
802
803 JumpCollector breakCollector = new JumpCollector(target);
804 JumpCollector continueCollector = new JumpCollector(target);
805 state.breakCollectors.add(breakCollector);
806 state.continueCollectors.add(continueCollector);
807
808 IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
809 buildBody(bodyBuilder);
810 assert(state.breakCollectors.last == breakCollector);
811 assert(state.continueCollectors.last == continueCollector);
812 state.breakCollectors.removeLast();
813 state.continueCollectors.removeLast();
814
815 // The binding of the continue continuation should occur as late as
816 // possible, that is, at the nearest common ancestor of all the continue
817 // sites in the body. However, that is difficult to compute here, so it
818 // is instead placed just outside the body of the body continuation.
819 bool hasContinues = !continueCollector.isEmpty;
820 IrBuilder updateBuilder = hasContinues
821 ? new IrBuilder.recursive(condBuilder)
822 : bodyBuilder;
823 buildUpdate(updateBuilder);
824
825 // Create body entry and loop exit continuations and a branch to them.
826 ir.Continuation bodyContinuation = new ir.Continuation([]);
827 ir.Continuation exitContinuation = new ir.Continuation([]);
828 ir.LetCont branch =
829 new ir.LetCont(exitContinuation,
830 new ir.LetCont(bodyContinuation,
831 new ir.Branch(new ir.IsTrue(condition),
832 bodyContinuation,
833 exitContinuation)));
834 // If there are breaks in the body, then there must be a join-point
835 // continuation for the normal exit and the breaks.
836 bool hasBreaks = !breakCollector.isEmpty;
837 ir.LetCont letJoin;
838 if (hasBreaks) {
839 letJoin = new ir.LetCont(null, branch);
840 condBuilder.add(letJoin);
841 condBuilder._current = branch;
842 } else {
843 condBuilder.add(branch);
844 }
845 ir.Continuation continueContinuation;
846 if (hasContinues) {
847 // If there are continues in the body, we need a named continue
848 // continuation as a join point.
849 continueContinuation = new ir.Continuation(updateBuilder._parameters);
850 if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
851 invokeFullJoin(continueContinuation, continueCollector);
852 }
853 ir.Continuation loopContinuation =
854 new ir.Continuation(condBuilder._parameters);
855 if (updateBuilder.isOpen) {
856 JumpCollector backEdges = new JumpCollector(null);
857 backEdges.addJump(updateBuilder);
858 invokeFullJoin(loopContinuation, backEdges, recursive: true);
859 }
860
861 // Fill in the body and possible continue continuation bodies. Do this
862 // only after it is guaranteed that they are not empty.
863 if (hasContinues) {
864 continueContinuation.body = updateBuilder._root;
865 bodyContinuation.body =
866 new ir.LetCont(continueContinuation, bodyBuilder._root);
867 } else {
868 bodyContinuation.body = bodyBuilder._root;
869 }
870
871 loopContinuation.body = condBuilder._root;
872 add(new ir.LetCont(loopContinuation,
873 new ir.InvokeContinuation(loopContinuation,
874 environment.index2value)));
875 if (hasBreaks) {
876 _current = branch;
877 environment = condBuilder.environment;
878 breakCollector.addJump(this);
879 letJoin.continuation = createJoin(environment.length, breakCollector);
880 _current = letJoin;
881 } else {
882 _current = condBuilder._current;
883 environment = condBuilder.environment;
884 }
885 }
886
887 /// Creates a for-in loop, `for (v in e) b`.
888 ///
889 /// [buildExpression] creates the expression, `e`. The variable, `v`, can
890 /// take one of three forms:
891 /// 1) `v` can be declared within the for-in statement, like in
892 /// `for (var v in e)`, in which case, [buildVariableDeclaration]
893 /// creates its declaration and [variableElement] is the element for
894 /// the declared variable,
895 /// 2) `v` is predeclared statically known variable, that is top-level,
896 /// static, or local variable, in which case [variableElement] is the
897 /// variable element, and [variableSelector] defines its write access,
898 /// 3) `v` is an instance variable in which case [variableSelector]
899 /// defines its write access.
900 /// [buildBody] creates the body, `b`, of the loop. The jump [target] is used
901 /// to identify which `break` and `continue` statements that have this for-in
902 /// statement as their target.
903 void buildForIn({SubbuildFunction buildExpression,
904 SubbuildFunction buildVariableDeclaration,
905 Element variableElement,
906 Selector variableSelector,
907 SubbuildFunction buildBody,
908 JumpTarget target}) {
909 // The for-in loop
910 //
911 // for (a in e) s;
912 //
913 // Is compiled analogously to:
914 //
915 // a = e.iterator;
916 // while (a.moveNext()) {
917 // var n0 = a.current;
918 // s;
919 // }
920
921 // The condition and body are delimited.
922 IrBuilder condBuilder = new IrBuilder.recursive(this);
923
924 ir.Primitive expressionReceiver = buildExpression(this);
925 List<ir.Primitive> emptyArguments = new List<ir.Primitive>();
926
927 ir.Parameter iterator = new ir.Parameter(null);
928 ir.Continuation iteratorInvoked = new ir.Continuation([iterator]);
929 add(new ir.LetCont(iteratorInvoked,
930 new ir.InvokeMethod(expressionReceiver,
931 new Selector.getter("iterator", null), iteratorInvoked,
932 emptyArguments)));
933
934 ir.Parameter condition = new ir.Parameter(null);
935 ir.Continuation moveNextInvoked = new ir.Continuation([condition]);
936 condBuilder.add(new ir.LetCont(moveNextInvoked,
937 new ir.InvokeMethod(iterator,
938 new Selector.call("moveNext", null, 0),
939 moveNextInvoked, emptyArguments)));
940
941 JumpCollector breakCollector = new JumpCollector(target);
942 JumpCollector continueCollector = new JumpCollector(target);
943 state.breakCollectors.add(breakCollector);
944 state.continueCollectors.add(continueCollector);
945
946 IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
947 if (buildVariableDeclaration != null) {
948 buildVariableDeclaration(bodyBuilder);
949 }
950
951 ir.Parameter currentValue = new ir.Parameter(null);
952 ir.Continuation currentInvoked = new ir.Continuation([currentValue]);
953 bodyBuilder.add(new ir.LetCont(currentInvoked,
954 new ir.InvokeMethod(iterator, new Selector.getter("current", null),
955 currentInvoked, emptyArguments)));
956 if (Elements.isLocal(variableElement)) {
957 bodyBuilder.buildLocalSet(variableElement, currentValue);
958 } else if (Elements.isStaticOrTopLevel(variableElement)) {
959 bodyBuilder.buildStaticSet(
960 variableElement, variableSelector, currentValue);
961 } else {
962 ir.Primitive receiver = bodyBuilder.buildThis();
963 bodyBuilder.buildDynamicSet(receiver, variableSelector, currentValue);
964 }
965
966 buildBody(bodyBuilder);
967 assert(state.breakCollectors.last == breakCollector);
968 assert(state.continueCollectors.last == continueCollector);
969 state.breakCollectors.removeLast();
970 state.continueCollectors.removeLast();
971
972 // Create body entry and loop exit continuations and a branch to them.
973 ir.Continuation bodyContinuation = new ir.Continuation([]);
974 ir.Continuation exitContinuation = new ir.Continuation([]);
975 ir.LetCont branch =
976 new ir.LetCont(exitContinuation,
977 new ir.LetCont(bodyContinuation,
978 new ir.Branch(new ir.IsTrue(condition),
979 bodyContinuation,
980 exitContinuation)));
981 // If there are breaks in the body, then there must be a join-point
982 // continuation for the normal exit and the breaks.
983 bool hasBreaks = !breakCollector.isEmpty;
984 ir.LetCont letJoin;
985 if (hasBreaks) {
986 letJoin = new ir.LetCont(null, branch);
987 condBuilder.add(letJoin);
988 condBuilder._current = branch;
989 } else {
990 condBuilder.add(branch);
991 }
992 ir.Continuation loopContinuation =
993 new ir.Continuation(condBuilder._parameters);
994 if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
995 invokeFullJoin(
996 loopContinuation, continueCollector, recursive: true);
997 bodyContinuation.body = bodyBuilder._root;
998
999 loopContinuation.body = condBuilder._root;
1000 add(new ir.LetCont(loopContinuation,
1001 new ir.InvokeContinuation(loopContinuation,
1002 environment.index2value)));
1003 if (hasBreaks) {
1004 _current = branch;
1005 environment = condBuilder.environment;
1006 breakCollector.addJump(this);
1007 letJoin.continuation = createJoin(environment.length, breakCollector);
1008 _current = letJoin;
1009 } else {
1010 _current = condBuilder._current;
1011 environment = condBuilder.environment;
1012 }
1013 }
1014
1015 /// Creates a while loop in which the condition and body are created by
1016 /// [buildCondition] and [buildBody], respectively.
1017 ///
1018 /// The jump [target] is used to identify which `break` and `continue`
1019 /// statements that have this `while` statement as their target.
1020 void buildWhile({SubbuildFunction buildCondition,
1021 SubbuildFunction buildBody,
1022 JumpTarget target}) {
1023 assert(isOpen);
1024 // While loops use four named continuations: the entry to the body, the
1025 // loop exit, the loop back edge (continue), and the loop exit (break).
1026 // The CPS translation of [[while (condition) body; successor]] is:
1027 //
1028 // let cont continue(x, ...) =
1029 // let prim cond = [[condition]] in
1030 // let cont break() = [[successor]] in
1031 // let cont exit() = break(v, ...) in
1032 // let cont body() = [[body]]; continue(v, ...) in
1033 // branch cond (body, exit) in
1034 // continue(v, ...)
1035 //
1036 // If there are no breaks in the body, the break continuation is inlined
1037 // in the exit continuation (i.e., the translation of the successor
1038 // statement occurs in the exit continuation).
1039
1040 // The condition and body are delimited.
1041 IrBuilder condBuilder = new IrBuilder.recursive(this);
1042 ir.Primitive condition = buildCondition(condBuilder);
1043
1044 JumpCollector breakCollector = new JumpCollector(target);
1045 JumpCollector continueCollector = new JumpCollector(target);
1046 state.breakCollectors.add(breakCollector);
1047 state.continueCollectors.add(continueCollector);
1048
1049 IrBuilder bodyBuilder = new IrBuilder.delimited(condBuilder);
1050 buildBody(bodyBuilder);
1051 assert(state.breakCollectors.last == breakCollector);
1052 assert(state.continueCollectors.last == continueCollector);
1053 state.breakCollectors.removeLast();
1054 state.continueCollectors.removeLast();
1055
1056 // Create body entry and loop exit continuations and a branch to them.
1057 ir.Continuation bodyContinuation = new ir.Continuation([]);
1058 ir.Continuation exitContinuation = new ir.Continuation([]);
1059 ir.LetCont branch =
1060 new ir.LetCont(exitContinuation,
1061 new ir.LetCont(bodyContinuation,
1062 new ir.Branch(new ir.IsTrue(condition),
1063 bodyContinuation,
1064 exitContinuation)));
1065 // If there are breaks in the body, then there must be a join-point
1066 // continuation for the normal exit and the breaks.
1067 bool hasBreaks = !breakCollector.isEmpty;
1068 ir.LetCont letJoin;
1069 if (hasBreaks) {
1070 letJoin = new ir.LetCont(null, branch);
1071 condBuilder.add(letJoin);
1072 condBuilder._current = branch;
1073 } else {
1074 condBuilder.add(branch);
1075 }
1076 ir.Continuation loopContinuation =
1077 new ir.Continuation(condBuilder._parameters);
1078 if (bodyBuilder.isOpen) continueCollector.addJump(bodyBuilder);
1079 invokeFullJoin(loopContinuation, continueCollector, recursive: true);
1080 bodyContinuation.body = bodyBuilder._root;
1081
1082 loopContinuation.body = condBuilder._root;
1083 add(new ir.LetCont(loopContinuation,
1084 new ir.InvokeContinuation(loopContinuation,
1085 environment.index2value)));
1086 if (hasBreaks) {
1087 _current = branch;
1088 environment = condBuilder.environment;
1089 breakCollector.addJump(this);
1090 letJoin.continuation = createJoin(environment.length, breakCollector);
1091 _current = letJoin;
1092 } else {
1093 _current = condBuilder._current;
1094 environment = condBuilder.environment;
1095 }
1096 }
1097
1098 /// Create a return statement `return value;` or `return;` if [value] is
1099 /// null.
1100 void buildReturn([ir.Primitive value]) {
1101 // Build(Return(e), C) = C'[InvokeContinuation(return, x)]
1102 // where (C', x) = Build(e, C)
1103 //
1104 // Return without a subexpression is translated as if it were return null.
1105 assert(isOpen);
1106 if (value == null) {
1107 value = buildNullLiteral();
1108 }
1109 add(new ir.InvokeContinuation(state.returnContinuation, [value]));
1110 _current = null;
1111 }
1112
1113 /// Create a blocks of [statements] by applying [build] to all reachable
1114 /// statements. The first statement is assumed to be reachable.
1115 // TODO(johnniwinther): Type [statements] as `Iterable` when `NodeList` uses
1116 // `List` instead of `Link`.
1117 void buildBlock(var statements, BuildFunction build) {
1118 // Build(Block(stamements), C) = C'
1119 // where C' = statements.fold(Build, C)
1120 assert(isOpen);
1121 return buildSequence(statements, build);
1122 }
1123
1124 /// Creates a sequence of [nodes] by applying [build] to all reachable nodes.
1125 ///
1126 /// The first node in the sequence does not need to be reachable.
1127 // TODO(johnniwinther): Type [nodes] as `Iterable` when `NodeList` uses
1128 // `List` instead of `Link`.
1129 void buildSequence(var nodes, BuildFunction build) {
1130 for (var node in nodes) {
1131 if (!isOpen) return;
1132 build(node);
1133 }
1134 }
1135
1136
1137 // Build(BreakStatement L, C) = C[InvokeContinuation(...)]
1138 //
1139 // The continuation and arguments are filled in later after translating
1140 // the body containing the break.
1141 bool buildBreak(JumpTarget target) {
1142 return buildJumpInternal(target, state.breakCollectors);
1143 }
1144
1145 // Build(ContinueStatement L, C) = C[InvokeContinuation(...)]
1146 //
1147 // The continuation and arguments are filled in later after translating
1148 // the body containing the continue.
1149 bool buildContinue(JumpTarget target) {
1150 return buildJumpInternal(target, state.continueCollectors);
1151 }
1152
1153 bool buildJumpInternal(JumpTarget target,
1154 Iterable<JumpCollector> collectors) {
1155 assert(isOpen);
1156 for (JumpCollector collector in collectors) {
1157 if (target == collector.target) {
1158 collector.addJump(this);
1159 return true;
1160 }
1161 }
1162 return false;
1163 }
1164
1165 /// Create a negation of [condition].
1166 ir.Primitive buildNegation(ir.Primitive condition) {
1167 // ! e is translated as e ? false : true
1168
1169 // Add a continuation parameter for the result of the expression.
1170 ir.Parameter resultParameter = new ir.Parameter(null);
1171
1172 ir.Continuation joinContinuation = new ir.Continuation([resultParameter]);
1173 ir.Continuation thenContinuation = new ir.Continuation([]);
1174 ir.Continuation elseContinuation = new ir.Continuation([]);
1175
1176 ir.Constant makeBoolConstant(bool value) {
1177 return new ir.Constant(new PrimitiveConstantExpression(
1178 state.constantSystem.createBool(value)));
1179 }
1180
1181 ir.Constant trueConstant = makeBoolConstant(true);
1182 ir.Constant falseConstant = makeBoolConstant(false);
1183
1184 thenContinuation.body = new ir.LetPrim(falseConstant)
1185 ..plug(new ir.InvokeContinuation(joinContinuation, [falseConstant]));
1186 elseContinuation.body = new ir.LetPrim(trueConstant)
1187 ..plug(new ir.InvokeContinuation(joinContinuation, [trueConstant]));
1188
1189 add(new ir.LetCont(joinContinuation,
1190 new ir.LetCont(thenContinuation,
1191 new ir.LetCont(elseContinuation,
1192 new ir.Branch(new ir.IsTrue(condition),
1193 thenContinuation,
1194 elseContinuation)))));
1195 return resultParameter;
1196 }
1197
1198 /// Creates a type test or type cast of [receiver] against [type].
1199 ///
1200 /// Set [isTypeTest] to `true` to create a type test and furthermore set
1201 /// [isNotCheck] to `true` to create a negated type test.
1202 ir.Primitive buildTypeOperator(ir.Primitive receiver,
1203 DartType type,
1204 {bool isTypeTest: false,
1205 bool isNotCheck: false}) {
1206 assert(isOpen);
1207 assert(isTypeTest != null);
1208 assert(!isNotCheck || isTypeTest);
1209 ir.Primitive check = _continueWithExpression(
1210 (k) => new ir.TypeOperator(receiver, type, k, isTypeTest: isTypeTest));
1211 return isNotCheck ? buildNegation(check) : check;
1212
1213 }
1214
1215 /// Create a lazy and/or expression. [leftValue] is the value of the left
1216 /// operand and [buildRightValue] is called to process the value of the right
1217 /// operand in the context of its own [IrBuilder].
1218 ir.Primitive buildLogicalOperator(
1219 ir.Primitive leftValue,
1220 ir.Primitive buildRightValue(IrBuilder builder),
1221 {bool isLazyOr: false}) {
1222 // e0 && e1 is translated as if e0 ? (e1 == true) : false.
1223 // e0 || e1 is translated as if e0 ? true : (e1 == true).
1224 // The translation must convert both e0 and e1 to booleans and handle
1225 // local variable assignments in e1.
1226
1227 IrBuilder rightBuilder = new IrBuilder.delimited(this);
1228 ir.Primitive rightValue = buildRightValue(rightBuilder);
1229 // A dummy empty target for the branch on the left subexpression branch.
1230 // This enables using the same infrastructure for join-point continuations
1231 // as in visitIf and visitConditional. It will hold a definition of the
1232 // appropriate constant and an invocation of the join-point continuation.
1233 IrBuilder emptyBuilder = new IrBuilder.delimited(this);
1234 // Dummy empty targets for right true and right false. They hold
1235 // definitions of the appropriate constant and an invocation of the
1236 // join-point continuation.
1237 IrBuilder rightTrueBuilder = new IrBuilder.delimited(rightBuilder);
1238 IrBuilder rightFalseBuilder = new IrBuilder.delimited(rightBuilder);
1239
1240 // If we don't evaluate the right subexpression, the value of the whole
1241 // expression is this constant.
1242 ir.Constant leftBool = emptyBuilder.buildBooleanLiteral(isLazyOr);
1243 // If we do evaluate the right subexpression, the value of the expression
1244 // is a true or false constant.
1245 ir.Constant rightTrue = rightTrueBuilder.buildBooleanLiteral(true);
1246 ir.Constant rightFalse = rightFalseBuilder.buildBooleanLiteral(false);
1247
1248 // Treat the result values as named values in the environment, so they
1249 // will be treated as arguments to the join-point continuation.
1250 assert(environment.length == emptyBuilder.environment.length);
1251 assert(environment.length == rightTrueBuilder.environment.length);
1252 assert(environment.length == rightFalseBuilder.environment.length);
1253 emptyBuilder.environment.extend(null, leftBool);
1254 rightTrueBuilder.environment.extend(null, rightTrue);
1255 rightFalseBuilder.environment.extend(null, rightFalse);
1256
1257 // Wire up two continuations for the left subexpression, two continuations
1258 // for the right subexpression, and a three-way join continuation.
1259 JumpCollector jumps = new JumpCollector(null);
1260 jumps.addJump(emptyBuilder);
1261 jumps.addJump(rightTrueBuilder);
1262 jumps.addJump(rightFalseBuilder);
1263 ir.Continuation joinContinuation =
1264 createJoin(environment.length + 1, jumps);
1265 ir.Continuation leftTrueContinuation = new ir.Continuation([]);
1266 ir.Continuation leftFalseContinuation = new ir.Continuation([]);
1267 ir.Continuation rightTrueContinuation = new ir.Continuation([]);
1268 ir.Continuation rightFalseContinuation = new ir.Continuation([]);
1269 rightTrueContinuation.body = rightTrueBuilder._root;
1270 rightFalseContinuation.body = rightFalseBuilder._root;
1271 // The right subexpression has two continuations.
1272 rightBuilder.add(
1273 new ir.LetCont(rightTrueContinuation,
1274 new ir.LetCont(rightFalseContinuation,
1275 new ir.Branch(new ir.IsTrue(rightValue),
1276 rightTrueContinuation,
1277 rightFalseContinuation))));
1278 // Depending on the operator, the left subexpression's continuations are
1279 // either the right subexpression or an invocation of the join-point
1280 // continuation.
1281 if (isLazyOr) {
1282 leftTrueContinuation.body = emptyBuilder._root;
1283 leftFalseContinuation.body = rightBuilder._root;
1284 } else {
1285 leftTrueContinuation.body = rightBuilder._root;
1286 leftFalseContinuation.body = emptyBuilder._root;
1287 }
1288
1289 add(new ir.LetCont(joinContinuation,
1290 new ir.LetCont(leftTrueContinuation,
1291 new ir.LetCont(leftFalseContinuation,
1292 new ir.Branch(new ir.IsTrue(leftValue),
1293 leftTrueContinuation,
1294 leftFalseContinuation)))));
1295 // There is always a join parameter for the result value, because it
1296 // is different on at least two paths.
1297 return joinContinuation.parameters.last;
1298 }
1299
1300 /// Creates an access to `this`.
1301 ir.Primitive buildThis() {
1302 assert(isOpen);
1303 ir.Primitive result = new ir.This();
1304 add(new ir.LetPrim(result));
1305 return result;
1306 }
1307
1308 /// Create a non-recursive join-point continuation.
1309 ///
1310 /// Given the environment length at the join point and a list of
1311 /// jumps that should reach the join point, create a join-point
1312 /// continuation. The join-point continuation has a parameter for each
1313 /// variable that has different values reaching on different paths.
1314 ///
1315 /// The jumps are uninitialized [ir.InvokeContinuation] expressions.
1316 /// They are filled in with the target continuation and appropriate
1317 /// arguments.
1318 ///
1319 /// As a side effect, the environment of this builder is updated to include
1320 /// the join-point continuation parameters.
1321 ir.Continuation createJoin(int environmentLength, JumpCollector jumps) {
1322 assert(jumps.length >= 2);
1323
1324 // Compute which values are identical on all paths reaching the join.
1325 // Handle the common case of a pair of contexts efficiently.
1326 Environment first = jumps.environments[0];
1327 Environment second = jumps.environments[1];
1328 assert(environmentLength <= first.length);
1329 assert(environmentLength <= second.length);
1330 assert(first.sameDomain(environmentLength, second));
1331 // A running count of the join-point parameters.
1332 int parameterCount = 0;
1333 // The null elements of common correspond to required parameters of the
1334 // join-point continuation.
1335 List<ir.Primitive> common =
1336 new List<ir.Primitive>.generate(environmentLength,
1337 (i) {
1338 ir.Primitive candidate = first[i];
1339 if (second[i] == candidate) {
1340 return candidate;
1341 } else {
1342 ++parameterCount;
1343 return null;
1344 }
1345 });
1346 // If there is already a parameter for each variable, the other
1347 // environments do not need to be considered.
1348 if (parameterCount < environmentLength) {
1349 for (int i = 0; i < environmentLength; ++i) {
1350 ir.Primitive candidate = common[i];
1351 if (candidate == null) continue;
1352 for (Environment current in jumps.environments.skip(2)) {
1353 assert(environmentLength <= current.length);
1354 assert(first.sameDomain(environmentLength, current));
1355 if (candidate != current[i]) {
1356 common[i] = null;
1357 ++parameterCount;
1358 break;
1359 }
1360 }
1361 if (parameterCount >= environmentLength) break;
1362 }
1363 }
1364
1365 // Create the join point continuation.
1366 List<ir.Parameter> parameters = <ir.Parameter>[];
1367 parameters.length = parameterCount;
1368 int index = 0;
1369 for (int i = 0; i < environmentLength; ++i) {
1370 if (common[i] == null) {
1371 parameters[index++] = new ir.Parameter(first.index2variable[i]);
1372 }
1373 }
1374 assert(index == parameterCount);
1375 ir.Continuation join = new ir.Continuation(parameters);
1376
1377 // Fill in all the continuation invocations.
1378 for (int i = 0; i < jumps.length; ++i) {
1379 Environment currentEnvironment = jumps.environments[i];
1380 ir.InvokeContinuation invoke = jumps.invocations[i];
1381 // Sharing this.environment with one of the invocations will not do
1382 // the right thing (this.environment has already been mutated).
1383 List<ir.Reference> arguments = <ir.Reference>[];
1384 arguments.length = parameterCount;
1385 int index = 0;
1386 for (int i = 0; i < environmentLength; ++i) {
1387 if (common[i] == null) {
1388 arguments[index++] = new ir.Reference(currentEnvironment[i]);
1389 }
1390 }
1391 invoke.continuation = new ir.Reference(join);
1392 invoke.arguments = arguments;
1393 }
1394
1395 // Mutate this.environment to be the environment at the join point. Do
1396 // this after adding the continuation invocations, because this.environment
1397 // might be collected by the jump collector and so the old environment
1398 // values are needed for the continuation invocation.
1399 //
1400 // Iterate to environment.length because environmentLength includes values
1401 // outside the environment which are 'phantom' variables used for the
1402 // values of expressions like &&, ||, and ?:.
1403 index = 0;
1404 for (int i = 0; i < environment.length; ++i) {
1405 if (common[i] == null) {
1406 environment.index2value[i] = parameters[index++];
1407 }
1408 }
1409
1410 return join;
1411 }
1412 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698