OLD | NEW |
| (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 // IrNodes are kept in a separate library to have precise control over their | |
6 // dependencies on other parts of the system. | |
7 library dart2js.ir_nodes; | |
8 | |
9 import '../constants/expressions.dart'; | |
10 import '../constants/values.dart' as values show ConstantValue; | |
11 import '../dart2jslib.dart' as dart2js show invariant; | |
12 import '../elements/elements.dart'; | |
13 import '../universe/universe.dart' show Selector, SelectorKind; | |
14 import '../dart_types.dart' show DartType, GenericType; | |
15 | |
16 abstract class Node { | |
17 static int hashCount = 0; | |
18 final int hashCode = hashCount = (hashCount + 1) & 0x3fffffff; | |
19 | |
20 /// A pointer to the parent node. Is null until set by optimization passes. | |
21 Node parent; | |
22 | |
23 accept(Visitor visitor); | |
24 } | |
25 | |
26 abstract class Expression extends Node { | |
27 Expression plug(Expression expr) => throw 'impossible'; | |
28 } | |
29 | |
30 /// The base class of things that variables can refer to: primitives, | |
31 /// continuations, function and continuation parameters, etc. | |
32 abstract class Definition extends Node { | |
33 // The head of a linked-list of occurrences, in no particular order. | |
34 Reference firstRef = null; | |
35 | |
36 bool get hasAtMostOneUse => firstRef == null || firstRef.next == null; | |
37 bool get hasExactlyOneUse => firstRef != null && firstRef.next == null; | |
38 bool get hasAtLeastOneUse => firstRef != null; | |
39 bool get hasMultipleUses => !hasAtMostOneUse; | |
40 | |
41 void substituteFor(Definition other) { | |
42 if (other.firstRef == null) return; | |
43 Reference previous, current = other.firstRef; | |
44 do { | |
45 current.definition = this; | |
46 previous = current; | |
47 current = current.next; | |
48 } while (current != null); | |
49 previous.next = firstRef; | |
50 if (firstRef != null) firstRef.previous = previous; | |
51 firstRef = other.firstRef; | |
52 } | |
53 } | |
54 | |
55 /// An expression that cannot throw or diverge and has no side-effects. | |
56 /// All primitives are named using the identity of the [Primitive] object. | |
57 /// | |
58 /// Primitives may allocate objects, this is not considered side-effect here. | |
59 /// | |
60 /// Although primitives may not mutate state, they may depend on state. | |
61 abstract class Primitive extends Definition { | |
62 /// The [VariableElement] or [ParameterElement] from which the primitive | |
63 /// binding originated. | |
64 Element hint; | |
65 | |
66 /// Register in which the variable binding this primitive can be allocated. | |
67 /// Separate register spaces are used for primitives with different [element]. | |
68 /// Assigned by [RegisterAllocator], is null before that phase. | |
69 int registerIndex; | |
70 | |
71 /// Use the given element as a hint for naming this primitive. | |
72 /// | |
73 /// Has no effect if this primitive already has a non-null [element]. | |
74 void useElementAsHint(Element hint) { | |
75 if (this.hint == null) { | |
76 this.hint = hint; | |
77 } | |
78 } | |
79 } | |
80 | |
81 /// Operands to invocations and primitives are always variables. They point to | |
82 /// their definition and are doubly-linked into a list of occurrences. | |
83 class Reference { | |
84 Definition definition; | |
85 Reference previous = null; | |
86 Reference next = null; | |
87 | |
88 /// A pointer to the parent node. Is null until set by optimization passes. | |
89 Node parent; | |
90 | |
91 Reference(this.definition) { | |
92 next = definition.firstRef; | |
93 if (next != null) next.previous = this; | |
94 definition.firstRef = this; | |
95 } | |
96 | |
97 /// Unlinks this reference from the list of occurrences. | |
98 void unlink() { | |
99 if (previous == null) { | |
100 assert(definition.firstRef == this); | |
101 definition.firstRef = next; | |
102 } else { | |
103 previous.next = next; | |
104 } | |
105 if (next != null) next.previous = previous; | |
106 } | |
107 } | |
108 | |
109 /// Binding a value (primitive or constant): 'let val x = V in E'. The bound | |
110 /// value is in scope in the body. | |
111 /// During one-pass construction a LetVal with an empty body is used to | |
112 /// represent one-level context 'let val x = V in []'. | |
113 class LetPrim extends Expression implements InteriorNode { | |
114 final Primitive primitive; | |
115 Expression body = null; | |
116 | |
117 LetPrim(this.primitive); | |
118 | |
119 Expression plug(Expression expr) { | |
120 assert(body == null); | |
121 return body = expr; | |
122 } | |
123 | |
124 accept(Visitor visitor) => visitor.visitLetPrim(this); | |
125 } | |
126 | |
127 | |
128 /// Binding a continuation: 'let cont k(v) = E in E'. The bound continuation | |
129 /// is in scope in the body and the continuation parameter is in scope in the | |
130 /// continuation body. | |
131 /// During one-pass construction a LetCont with an empty continuation body is | |
132 /// used to represent the one-level context 'let cont k(v) = [] in E'. | |
133 class LetCont extends Expression implements InteriorNode { | |
134 Continuation continuation; | |
135 Expression body; | |
136 | |
137 LetCont(this.continuation, this.body); | |
138 | |
139 Expression plug(Expression expr) { | |
140 assert(continuation != null && continuation.body == null); | |
141 return continuation.body = expr; | |
142 } | |
143 | |
144 accept(Visitor visitor) => visitor.visitLetCont(this); | |
145 } | |
146 | |
147 abstract class Invoke { | |
148 Selector get selector; | |
149 List<Reference> get arguments; | |
150 } | |
151 | |
152 /// Represents a node with a child node, which can be accessed through the | |
153 /// `body` member. A typical usage is when removing a node from the CPS graph: | |
154 /// | |
155 /// Node child = node.body; | |
156 /// InteriorNode parent = node.parent; | |
157 /// | |
158 /// child.parent = parent; | |
159 /// parent.body = child; | |
160 abstract class InteriorNode implements Node { | |
161 Expression body; | |
162 } | |
163 | |
164 /// Invoke a static function or static field getter/setter. | |
165 class InvokeStatic extends Expression implements Invoke { | |
166 /// [FunctionElement] or [FieldElement]. | |
167 final Entity target; | |
168 | |
169 /** | |
170 * The selector encodes how the function is invoked: number of positional | |
171 * arguments, names used in named arguments. This information is required | |
172 * to build the [StaticCallSiteTypeInformation] for the inference graph. | |
173 */ | |
174 final Selector selector; | |
175 | |
176 final Reference continuation; | |
177 final List<Reference> arguments; | |
178 | |
179 InvokeStatic(this.target, this.selector, Continuation cont, | |
180 List<Definition> args) | |
181 : continuation = new Reference(cont), | |
182 arguments = _referenceList(args) { | |
183 assert(target is ErroneousElement || selector.name == target.name); | |
184 } | |
185 | |
186 accept(Visitor visitor) => visitor.visitInvokeStatic(this); | |
187 } | |
188 | |
189 /// Invoke a method, operator, getter, setter, or index getter/setter. | |
190 /// Converting a method to a function object is treated as a getter invocation. | |
191 class InvokeMethod extends Expression implements Invoke { | |
192 final Reference receiver; | |
193 final Selector selector; | |
194 final Reference continuation; | |
195 final List<Reference> arguments; | |
196 | |
197 InvokeMethod(Definition receiver, | |
198 this.selector, | |
199 Continuation cont, | |
200 List<Definition> args) | |
201 : receiver = new Reference(receiver), | |
202 continuation = new Reference(cont), | |
203 arguments = _referenceList(args) { | |
204 assert(selector != null); | |
205 assert(selector.kind == SelectorKind.CALL || | |
206 selector.kind == SelectorKind.OPERATOR || | |
207 (selector.kind == SelectorKind.GETTER && arguments.isEmpty) || | |
208 (selector.kind == SelectorKind.SETTER && arguments.length == 1) || | |
209 (selector.kind == SelectorKind.INDEX && arguments.length == 1) || | |
210 (selector.kind == SelectorKind.INDEX && arguments.length == 2)); | |
211 } | |
212 | |
213 accept(Visitor visitor) => visitor.visitInvokeMethod(this); | |
214 } | |
215 | |
216 /// Invoke a method, operator, getter, setter, or index getter/setter from the | |
217 /// super class in tail position. | |
218 class InvokeSuperMethod extends Expression implements Invoke { | |
219 final Selector selector; | |
220 final Reference continuation; | |
221 final List<Reference> arguments; | |
222 | |
223 InvokeSuperMethod(this.selector, | |
224 Continuation cont, | |
225 List<Definition> args) | |
226 : continuation = new Reference(cont), | |
227 arguments = _referenceList(args) { | |
228 assert(selector != null); | |
229 assert(selector.kind == SelectorKind.CALL || | |
230 selector.kind == SelectorKind.OPERATOR || | |
231 (selector.kind == SelectorKind.GETTER && arguments.isEmpty) || | |
232 (selector.kind == SelectorKind.SETTER && arguments.length == 1) || | |
233 (selector.kind == SelectorKind.INDEX && arguments.length == 1) || | |
234 (selector.kind == SelectorKind.INDEX && arguments.length == 2)); | |
235 } | |
236 | |
237 accept(Visitor visitor) => visitor.visitInvokeSuperMethod(this); | |
238 } | |
239 | |
240 /// Non-const call to a constructor. The [target] may be a generative | |
241 /// constructor, factory, or redirecting factory. | |
242 class InvokeConstructor extends Expression implements Invoke { | |
243 final DartType type; | |
244 final FunctionElement target; | |
245 final Reference continuation; | |
246 final List<Reference> arguments; | |
247 final Selector selector; | |
248 | |
249 /// The class being instantiated. This is the same as `target.enclosingClass` | |
250 /// and `type.element`. | |
251 ClassElement get targetClass => target.enclosingElement; | |
252 | |
253 /// True if this is an invocation of a factory constructor. | |
254 bool get isFactory => target.isFactoryConstructor; | |
255 | |
256 InvokeConstructor(this.type, | |
257 this.target, | |
258 this.selector, | |
259 Continuation cont, | |
260 List<Definition> args) | |
261 : continuation = new Reference(cont), | |
262 arguments = _referenceList(args) { | |
263 assert(dart2js.invariant(target, | |
264 target.isErroneous || target.isConstructor, | |
265 message: "Constructor invocation target is not a constructor: " | |
266 "$target.")); | |
267 assert(dart2js.invariant(target, | |
268 target.isErroneous || | |
269 type.isDynamic || | |
270 type.element == target.enclosingClass.declaration, | |
271 message: "Constructor invocation type ${type} does not match enclosing " | |
272 "class of target ${target}.")); | |
273 } | |
274 | |
275 accept(Visitor visitor) => visitor.visitInvokeConstructor(this); | |
276 } | |
277 | |
278 /// "as" casts and "is" checks. | |
279 // We might want to turn "is"-checks into a [Primitive] as it can never diverge. | |
280 // But then we need to special-case for is-checks with an erroneous .type as | |
281 // these will throw. | |
282 class TypeOperator extends Expression { | |
283 final Reference receiver; | |
284 final DartType type; | |
285 final Reference continuation; | |
286 // TODO(johnniwinther): Use `Operator` class to encapsule the operator type. | |
287 final bool isTypeTest; | |
288 | |
289 TypeOperator(Primitive receiver, | |
290 this.type, | |
291 Continuation cont, | |
292 {bool this.isTypeTest}) | |
293 : this.receiver = new Reference(receiver), | |
294 this.continuation = new Reference(cont) { | |
295 assert(isTypeTest != null); | |
296 } | |
297 | |
298 bool get isTypeCast => !isTypeTest; | |
299 | |
300 accept(Visitor visitor) => visitor.visitTypeOperator(this); | |
301 } | |
302 | |
303 /// Invoke [toString] on each argument and concatenate the results. | |
304 class ConcatenateStrings extends Expression { | |
305 final Reference continuation; | |
306 final List<Reference> arguments; | |
307 | |
308 ConcatenateStrings(Continuation cont, List<Definition> args) | |
309 : continuation = new Reference(cont), | |
310 arguments = _referenceList(args); | |
311 | |
312 accept(Visitor visitor) => visitor.visitConcatenateStrings(this); | |
313 } | |
314 | |
315 /// Gets the value from a closure variable. The identity of the variable is | |
316 /// determined by a [Local]. | |
317 /// | |
318 /// Closure variables can be seen as ref cells that are not first-class values. | |
319 /// A [LetPrim] with a [GetClosureVariable] can then be seen as: | |
320 /// | |
321 /// let prim p = ![variable] in [body] | |
322 /// | |
323 class GetClosureVariable extends Primitive { | |
324 final Local variable; | |
325 | |
326 GetClosureVariable(this.variable) { | |
327 assert(variable != null); | |
328 } | |
329 | |
330 accept(Visitor visitor) => visitor.visitGetClosureVariable(this); | |
331 } | |
332 | |
333 /// Assign or declare a closure variable. The identity of the variable is | |
334 /// determined by a [Local]. | |
335 /// | |
336 /// Closure variables can be seen as ref cells that are not first-class values. | |
337 /// If [isDeclaration], this can seen as a let binding: | |
338 /// | |
339 /// let [variable] = ref [value] in [body] | |
340 /// | |
341 /// And otherwise, it can be seen as a dereferencing assignment: | |
342 /// | |
343 /// { ![variable] := [value]; [body] } | |
344 /// | |
345 /// Closure variables without a declaring [SetClosureVariable] are implicitly | |
346 /// declared at the entry to the [variable]'s enclosing function. | |
347 class SetClosureVariable extends Expression implements InteriorNode { | |
348 final Local variable; | |
349 final Reference value; | |
350 Expression body; | |
351 | |
352 /// If true, this declares a new copy of the closure variable. If so, all | |
353 /// uses of the closure variable must occur in the [body]. | |
354 /// | |
355 /// There can be at most one declaration per closure variable. If there is no | |
356 /// declaration, only one copy exists (per function execution). It is best to | |
357 /// avoid declaring closure variables if it is not necessary. | |
358 final bool isDeclaration; | |
359 | |
360 SetClosureVariable(this.variable, Primitive value, | |
361 {this.isDeclaration : false }) | |
362 : this.value = new Reference(value) { | |
363 assert(variable != null); | |
364 } | |
365 | |
366 accept(Visitor visitor) => visitor.visitSetClosureVariable(this); | |
367 | |
368 Expression plug(Expression expr) { | |
369 assert(body == null); | |
370 return body = expr; | |
371 } | |
372 } | |
373 | |
374 /// Create a potentially recursive function and store it in a closure variable. | |
375 /// The function can access itself using [GetClosureVariable] on [variable]. | |
376 /// There must not exist a [SetClosureVariable] to [variable]. | |
377 /// | |
378 /// This can be seen as a let rec binding: | |
379 /// | |
380 /// let rec [variable] = [definition] in [body] | |
381 /// | |
382 class DeclareFunction extends Expression implements InteriorNode { | |
383 final Local variable; | |
384 final FunctionDefinition definition; | |
385 Expression body; | |
386 | |
387 DeclareFunction(this.variable, this.definition); | |
388 | |
389 Expression plug(Expression expr) { | |
390 assert(body == null); | |
391 return body = expr; | |
392 } | |
393 | |
394 accept(Visitor visitor) => visitor.visitDeclareFunction(this); | |
395 } | |
396 | |
397 /// Invoke a continuation in tail position. | |
398 class InvokeContinuation extends Expression { | |
399 Reference continuation; | |
400 List<Reference> arguments; | |
401 | |
402 // An invocation of a continuation is recursive if it occurs in the body of | |
403 // the continuation itself. | |
404 bool isRecursive; | |
405 | |
406 InvokeContinuation(Continuation cont, List<Definition> args, | |
407 {recursive: false}) | |
408 : continuation = new Reference(cont), | |
409 arguments = _referenceList(args), | |
410 isRecursive = recursive { | |
411 assert(cont.parameters == null || | |
412 cont.parameters.length == args.length); | |
413 if (recursive) cont.isRecursive = true; | |
414 } | |
415 | |
416 /// A continuation invocation whose target and arguments will be filled | |
417 /// in later. | |
418 /// | |
419 /// Used as a placeholder for a jump whose target is not yet created | |
420 /// (e.g., in the translation of break and continue). | |
421 InvokeContinuation.uninitialized({recursive: false}) | |
422 : continuation = null, | |
423 arguments = null, | |
424 isRecursive = recursive; | |
425 | |
426 accept(Visitor visitor) => visitor.visitInvokeContinuation(this); | |
427 } | |
428 | |
429 /// The base class of things which can be tested and branched on. | |
430 abstract class Condition extends Node { | |
431 } | |
432 | |
433 class IsTrue extends Condition { | |
434 final Reference value; | |
435 | |
436 IsTrue(Definition val) : value = new Reference(val); | |
437 | |
438 accept(Visitor visitor) => visitor.visitIsTrue(this); | |
439 } | |
440 | |
441 /// Choose between a pair of continuations based on a condition value. | |
442 class Branch extends Expression { | |
443 final Condition condition; | |
444 final Reference trueContinuation; | |
445 final Reference falseContinuation; | |
446 | |
447 Branch(this.condition, Continuation trueCont, Continuation falseCont) | |
448 : trueContinuation = new Reference(trueCont), | |
449 falseContinuation = new Reference(falseCont); | |
450 | |
451 accept(Visitor visitor) => visitor.visitBranch(this); | |
452 } | |
453 | |
454 class Constant extends Primitive { | |
455 final ConstantExpression expression; | |
456 | |
457 Constant(this.expression); | |
458 | |
459 values.ConstantValue get value => expression.value; | |
460 | |
461 accept(Visitor visitor) => visitor.visitConstant(this); | |
462 } | |
463 | |
464 class This extends Primitive { | |
465 This(); | |
466 | |
467 accept(Visitor visitor) => visitor.visitThis(this); | |
468 } | |
469 | |
470 /// Reify the given type variable as a [Type]. | |
471 /// This depends on the current binding of 'this'. | |
472 class ReifyTypeVar extends Primitive { | |
473 final TypeVariableElement typeVariable; | |
474 | |
475 ReifyTypeVar(this.typeVariable); | |
476 | |
477 values.ConstantValue get constant => null; | |
478 | |
479 accept(Visitor visitor) => visitor.visitReifyTypeVar(this); | |
480 } | |
481 | |
482 class LiteralList extends Primitive { | |
483 /// The List type being created; this is not the type argument. | |
484 final GenericType type; | |
485 final List<Reference> values; | |
486 | |
487 LiteralList(this.type, Iterable<Primitive> values) | |
488 : this.values = _referenceList(values); | |
489 | |
490 accept(Visitor visitor) => visitor.visitLiteralList(this); | |
491 } | |
492 | |
493 class LiteralMapEntry { | |
494 final Reference key; | |
495 final Reference value; | |
496 | |
497 LiteralMapEntry(Primitive key, Primitive value) | |
498 : this.key = new Reference(key), | |
499 this.value = new Reference(value); | |
500 } | |
501 | |
502 class LiteralMap extends Primitive { | |
503 final GenericType type; | |
504 final List<LiteralMapEntry> entries; | |
505 | |
506 LiteralMap(this.type, this.entries); | |
507 | |
508 accept(Visitor visitor) => visitor.visitLiteralMap(this); | |
509 } | |
510 | |
511 /// Create a non-recursive function. | |
512 class CreateFunction extends Primitive { | |
513 final FunctionDefinition definition; | |
514 | |
515 CreateFunction(this.definition); | |
516 | |
517 accept(Visitor visitor) => visitor.visitCreateFunction(this); | |
518 } | |
519 | |
520 class Parameter extends Primitive { | |
521 Parameter(Element element) { | |
522 super.hint = element; | |
523 } | |
524 | |
525 accept(Visitor visitor) => visitor.visitParameter(this); | |
526 } | |
527 | |
528 /// Continuations are normally bound by 'let cont'. A continuation with one | |
529 /// parameter and no body is used to represent a function's return continuation. | |
530 /// The return continuation is bound by the Function, not by 'let cont'. | |
531 class Continuation extends Definition implements InteriorNode { | |
532 final List<Parameter> parameters; | |
533 Expression body = null; | |
534 | |
535 // A continuation is recursive if it has any recursive invocations. | |
536 bool isRecursive = false; | |
537 | |
538 bool get isReturnContinuation => body == null; | |
539 | |
540 Continuation(this.parameters); | |
541 | |
542 Continuation.retrn() : parameters = <Parameter>[new Parameter(null)]; | |
543 | |
544 accept(Visitor visitor) => visitor.visitContinuation(this); | |
545 } | |
546 | |
547 /// A function definition, consisting of parameters and a body. The parameters | |
548 /// include a distinguished continuation parameter. | |
549 class FunctionDefinition extends Node implements InteriorNode { | |
550 final FunctionElement element; | |
551 final Continuation returnContinuation; | |
552 final List<Parameter> parameters; | |
553 Expression body; | |
554 final List<ConstDeclaration> localConstants; | |
555 | |
556 /// Values for optional parameters. | |
557 final List<ConstantExpression> defaultParameterValues; | |
558 | |
559 FunctionDefinition(this.element, this.returnContinuation, | |
560 this.parameters, this.body, this.localConstants, | |
561 this.defaultParameterValues); | |
562 | |
563 FunctionDefinition.abstract(this.element, | |
564 this.parameters, | |
565 this.defaultParameterValues) | |
566 : this.returnContinuation = null, | |
567 this.localConstants = const <ConstDeclaration>[]; | |
568 | |
569 accept(Visitor visitor) => visitor.visitFunctionDefinition(this); | |
570 | |
571 /// Returns `true` if this function is abstract. | |
572 /// | |
573 /// If `true`, [body] and [returnContinuation] are `null` and [localConstants] | |
574 /// is empty. | |
575 bool get isAbstract => body == null; | |
576 } | |
577 | |
578 List<Reference> _referenceList(Iterable<Definition> definitions) { | |
579 return definitions.map((e) => new Reference(e)).toList(); | |
580 } | |
581 | |
582 abstract class Visitor<T> { | |
583 T visit(Node node) => node.accept(this); | |
584 // Abstract classes. | |
585 T visitNode(Node node) => null; | |
586 T visitExpression(Expression node) => visitNode(node); | |
587 T visitDefinition(Definition node) => visitNode(node); | |
588 T visitPrimitive(Primitive node) => visitDefinition(node); | |
589 T visitCondition(Condition node) => visitNode(node); | |
590 | |
591 // Concrete classes. | |
592 T visitFunctionDefinition(FunctionDefinition node) => visitNode(node); | |
593 | |
594 // Expressions. | |
595 T visitLetPrim(LetPrim node) => visitExpression(node); | |
596 T visitLetCont(LetCont node) => visitExpression(node); | |
597 T visitInvokeStatic(InvokeStatic node) => visitExpression(node); | |
598 T visitInvokeContinuation(InvokeContinuation node) => visitExpression(node); | |
599 T visitInvokeMethod(InvokeMethod node) => visitExpression(node); | |
600 T visitInvokeSuperMethod(InvokeSuperMethod node) => visitExpression(node); | |
601 T visitInvokeConstructor(InvokeConstructor node) => visitExpression(node); | |
602 T visitConcatenateStrings(ConcatenateStrings node) => visitExpression(node); | |
603 T visitBranch(Branch node) => visitExpression(node); | |
604 T visitTypeOperator(TypeOperator node) => visitExpression(node); | |
605 T visitSetClosureVariable(SetClosureVariable node) => visitExpression(node); | |
606 T visitDeclareFunction(DeclareFunction node) => visitExpression(node); | |
607 | |
608 // Definitions. | |
609 T visitLiteralList(LiteralList node) => visitPrimitive(node); | |
610 T visitLiteralMap(LiteralMap node) => visitPrimitive(node); | |
611 T visitConstant(Constant node) => visitPrimitive(node); | |
612 T visitThis(This node) => visitPrimitive(node); | |
613 T visitReifyTypeVar(ReifyTypeVar node) => visitPrimitive(node); | |
614 T visitCreateFunction(CreateFunction node) => visitPrimitive(node); | |
615 T visitGetClosureVariable(GetClosureVariable node) => visitPrimitive(node); | |
616 T visitParameter(Parameter node) => visitPrimitive(node); | |
617 T visitContinuation(Continuation node) => visitDefinition(node); | |
618 | |
619 // Conditions. | |
620 T visitIsTrue(IsTrue node) => visitCondition(node); | |
621 } | |
622 | |
623 /// Recursively visits the entire CPS term, and calls abstract `process*` | |
624 /// (i.e. `processLetPrim`) functions in pre-order. | |
625 abstract class RecursiveVisitor extends Visitor { | |
626 // Ensures that RecursiveVisitor contains overrides for all relevant nodes. | |
627 // As a rule of thumb, nodes with structure to traverse should be overridden | |
628 // with the appropriate visits in this class (for example, visitLetCont), | |
629 // while leaving other nodes for subclasses (i.e., visitLiteralList). | |
630 visitNode(Node node) { | |
631 throw "RecursiveVisitor is stale, add missing visit overrides"; | |
632 } | |
633 | |
634 processReference(Reference ref) {} | |
635 | |
636 processFunctionDefinition(FunctionDefinition node) {} | |
637 visitFunctionDefinition(FunctionDefinition node) { | |
638 processFunctionDefinition(node); | |
639 node.parameters.forEach(visitParameter); | |
640 visit(node.body); | |
641 } | |
642 | |
643 // Expressions. | |
644 | |
645 processLetPrim(LetPrim node) {} | |
646 visitLetPrim(LetPrim node) { | |
647 processLetPrim(node); | |
648 visit(node.primitive); | |
649 visit(node.body); | |
650 } | |
651 | |
652 processLetCont(LetCont node) {} | |
653 visitLetCont(LetCont node) { | |
654 processLetCont(node); | |
655 visit(node.continuation); | |
656 visit(node.body); | |
657 } | |
658 | |
659 processInvokeStatic(InvokeStatic node) {} | |
660 visitInvokeStatic(InvokeStatic node) { | |
661 processInvokeStatic(node); | |
662 processReference(node.continuation); | |
663 node.arguments.forEach(processReference); | |
664 } | |
665 | |
666 processInvokeContinuation(InvokeContinuation node) {} | |
667 visitInvokeContinuation(InvokeContinuation node) { | |
668 processInvokeContinuation(node); | |
669 processReference(node.continuation); | |
670 node.arguments.forEach(processReference); | |
671 } | |
672 | |
673 processInvokeMethod(InvokeMethod node) {} | |
674 visitInvokeMethod(InvokeMethod node) { | |
675 processInvokeMethod(node); | |
676 processReference(node.receiver); | |
677 processReference(node.continuation); | |
678 node.arguments.forEach(processReference); | |
679 } | |
680 | |
681 processInvokeSuperMethod(InvokeSuperMethod node) {} | |
682 visitInvokeSuperMethod(InvokeSuperMethod node) { | |
683 processInvokeSuperMethod(node); | |
684 processReference(node.continuation); | |
685 node.arguments.forEach(processReference); | |
686 } | |
687 | |
688 processInvokeConstructor(InvokeConstructor node) {} | |
689 visitInvokeConstructor(InvokeConstructor node) { | |
690 processInvokeConstructor(node); | |
691 processReference(node.continuation); | |
692 node.arguments.forEach(processReference); | |
693 } | |
694 | |
695 processConcatenateStrings(ConcatenateStrings node) {} | |
696 visitConcatenateStrings(ConcatenateStrings node) { | |
697 processConcatenateStrings(node); | |
698 processReference(node.continuation); | |
699 node.arguments.forEach(processReference); | |
700 } | |
701 | |
702 | |
703 processBranch(Branch node) {} | |
704 visitBranch(Branch node) { | |
705 processBranch(node); | |
706 processReference(node.trueContinuation); | |
707 processReference(node.falseContinuation); | |
708 visit(node.condition); | |
709 } | |
710 | |
711 processTypeOperator(TypeOperator node) {} | |
712 visitTypeOperator(TypeOperator node) { | |
713 processTypeOperator(node); | |
714 processReference(node.continuation); | |
715 processReference(node.receiver); | |
716 } | |
717 | |
718 processSetClosureVariable(SetClosureVariable node) {} | |
719 visitSetClosureVariable(SetClosureVariable node) { | |
720 processSetClosureVariable(node); | |
721 processReference(node.value); | |
722 visit(node.body); | |
723 } | |
724 | |
725 processDeclareFunction(DeclareFunction node) {} | |
726 visitDeclareFunction(DeclareFunction node) { | |
727 processDeclareFunction(node); | |
728 visit(node.definition); | |
729 visit(node.body); | |
730 } | |
731 | |
732 // Definitions. | |
733 | |
734 processLiteralList(LiteralList node) {} | |
735 visitLiteralList(LiteralList node) { | |
736 processLiteralList(node); | |
737 node.values.forEach(processReference); | |
738 } | |
739 | |
740 processLiteralMap(LiteralMap node) {} | |
741 visitLiteralMap(LiteralMap node) { | |
742 processLiteralMap(node); | |
743 for (LiteralMapEntry entry in node.entries) { | |
744 processReference(entry.key); | |
745 processReference(entry.value); | |
746 } | |
747 } | |
748 | |
749 processConstant(Constant node) {} | |
750 visitConstant(Constant node) => processConstant(node); | |
751 | |
752 processThis(This node) {} | |
753 visitThis(This node) => processThis(node); | |
754 | |
755 processReifyTypeVar(ReifyTypeVar node) {} | |
756 visitReifyTypeVar(ReifyTypeVar node) => processReifyTypeVar(node); | |
757 | |
758 processCreateFunction(CreateFunction node) {} | |
759 visitCreateFunction(CreateFunction node) { | |
760 processCreateFunction(node); | |
761 visit(node.definition); | |
762 } | |
763 | |
764 processGetClosureVariable(GetClosureVariable node) {} | |
765 visitGetClosureVariable(GetClosureVariable node) => | |
766 processGetClosureVariable(node); | |
767 | |
768 processParameter(Parameter node) {} | |
769 visitParameter(Parameter node) => processParameter(node); | |
770 | |
771 processContinuation(Continuation node) {} | |
772 visitContinuation(Continuation node) { | |
773 processContinuation(node); | |
774 node.parameters.forEach(visitParameter); | |
775 visit(node.body); | |
776 } | |
777 | |
778 // Conditions. | |
779 | |
780 processIsTrue(IsTrue node) {} | |
781 visitIsTrue(IsTrue node) { | |
782 processIsTrue(node); | |
783 processReference(node.value); | |
784 } | |
785 } | |
786 | |
787 /// Keeps track of currently unused register indices. | |
788 class RegisterArray { | |
789 int nextIndex = 0; | |
790 final List<int> freeStack = <int>[]; | |
791 | |
792 /// Returns an index that is currently unused. | |
793 int makeIndex() { | |
794 if (freeStack.isEmpty) { | |
795 return nextIndex++; | |
796 } else { | |
797 return freeStack.removeLast(); | |
798 } | |
799 } | |
800 | |
801 void releaseIndex(int index) { | |
802 freeStack.add(index); | |
803 } | |
804 } | |
805 | |
806 /// Assigns indices to each primitive in the IR such that primitives that are | |
807 /// live simultaneously never get assigned the same index. | |
808 /// This information is used by the dart tree builder to generate fewer | |
809 /// redundant variables. | |
810 /// Currently, the liveness analysis is very simple and is often inadequate | |
811 /// for removing all of the redundant variables. | |
812 class RegisterAllocator extends Visitor { | |
813 /// Separate register spaces for each source-level variable/parameter. | |
814 /// Note that null is used as key for primitives without elements. | |
815 final Map<Element, RegisterArray> elementRegisters = | |
816 <Element, RegisterArray>{}; | |
817 | |
818 RegisterArray getRegisterArray(Element element) { | |
819 RegisterArray registers = elementRegisters[element]; | |
820 if (registers == null) { | |
821 registers = new RegisterArray(); | |
822 elementRegisters[element] = registers; | |
823 } | |
824 return registers; | |
825 } | |
826 | |
827 void allocate(Primitive primitive) { | |
828 if (primitive.registerIndex == null) { | |
829 primitive.registerIndex = getRegisterArray(primitive.hint).makeIndex(); | |
830 } | |
831 } | |
832 | |
833 void release(Primitive primitive) { | |
834 // Do not share indices for temporaries as this may obstruct inlining. | |
835 if (primitive.hint == null) return; | |
836 if (primitive.registerIndex != null) { | |
837 getRegisterArray(primitive.hint).releaseIndex(primitive.registerIndex); | |
838 } | |
839 } | |
840 | |
841 void visitReference(Reference reference) { | |
842 allocate(reference.definition); | |
843 } | |
844 | |
845 void visitFunctionDefinition(FunctionDefinition node) { | |
846 if (!node.isAbstract) { | |
847 visit(node.body); | |
848 } | |
849 node.parameters.forEach(allocate); // Assign indices to unused parameters. | |
850 elementRegisters.clear(); | |
851 } | |
852 | |
853 void visitLetPrim(LetPrim node) { | |
854 visit(node.body); | |
855 release(node.primitive); | |
856 visit(node.primitive); | |
857 } | |
858 | |
859 void visitLetCont(LetCont node) { | |
860 visit(node.continuation); | |
861 visit(node.body); | |
862 } | |
863 | |
864 void visitInvokeStatic(InvokeStatic node) { | |
865 node.arguments.forEach(visitReference); | |
866 } | |
867 | |
868 void visitInvokeContinuation(InvokeContinuation node) { | |
869 node.arguments.forEach(visitReference); | |
870 } | |
871 | |
872 void visitInvokeMethod(InvokeMethod node) { | |
873 visitReference(node.receiver); | |
874 node.arguments.forEach(visitReference); | |
875 } | |
876 | |
877 void visitInvokeSuperMethod(InvokeSuperMethod node) { | |
878 node.arguments.forEach(visitReference); | |
879 } | |
880 | |
881 void visitInvokeConstructor(InvokeConstructor node) { | |
882 node.arguments.forEach(visitReference); | |
883 } | |
884 | |
885 void visitConcatenateStrings(ConcatenateStrings node) { | |
886 node.arguments.forEach(visitReference); | |
887 } | |
888 | |
889 void visitBranch(Branch node) { | |
890 visit(node.condition); | |
891 } | |
892 | |
893 void visitLiteralList(LiteralList node) { | |
894 node.values.forEach(visitReference); | |
895 } | |
896 | |
897 void visitLiteralMap(LiteralMap node) { | |
898 for (LiteralMapEntry entry in node.entries) { | |
899 visitReference(entry.key); | |
900 visitReference(entry.value); | |
901 } | |
902 } | |
903 | |
904 void visitTypeOperator(TypeOperator node) { | |
905 visitReference(node.receiver); | |
906 } | |
907 | |
908 void visitConstant(Constant node) { | |
909 } | |
910 | |
911 void visitThis(This node) { | |
912 } | |
913 | |
914 void visitReifyTypeVar(ReifyTypeVar node) { | |
915 } | |
916 | |
917 void visitCreateFunction(CreateFunction node) { | |
918 new RegisterAllocator().visit(node.definition); | |
919 } | |
920 | |
921 void visitGetClosureVariable(GetClosureVariable node) { | |
922 } | |
923 | |
924 void visitSetClosureVariable(SetClosureVariable node) { | |
925 visit(node.body); | |
926 visitReference(node.value); | |
927 } | |
928 | |
929 void visitDeclareFunction(DeclareFunction node) { | |
930 new RegisterAllocator().visit(node.definition); | |
931 visit(node.body); | |
932 } | |
933 | |
934 void visitParameter(Parameter node) { | |
935 throw "Parameters should not be visited by RegisterAllocator"; | |
936 } | |
937 | |
938 void visitContinuation(Continuation node) { | |
939 visit(node.body); | |
940 | |
941 // Arguments get allocated left-to-right, so we release parameters | |
942 // right-to-left. This increases the likelihood that arguments can be | |
943 // transferred without intermediate assignments. | |
944 for (int i = node.parameters.length - 1; i >= 0; --i) { | |
945 release(node.parameters[i]); | |
946 } | |
947 } | |
948 | |
949 void visitIsTrue(IsTrue node) { | |
950 visitReference(node.value); | |
951 } | |
952 | |
953 } | |
954 | |
OLD | NEW |