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 final String operator; | |
287 | |
288 TypeOperator(this.operator, | |
289 Primitive receiver, | |
290 this.type, | |
291 Continuation cont) | |
292 : this.receiver = new Reference(receiver), | |
293 this.continuation = new Reference(cont) { | |
294 assert(operator == "is" || operator == "as"); | |
295 } | |
296 | |
297 accept(Visitor visitor) => visitor.visitTypeOperator(this); | |
298 } | |
299 | |
300 /// Invoke [toString] on each argument and concatenate the results. | |
301 class ConcatenateStrings extends Expression { | |
302 final Reference continuation; | |
303 final List<Reference> arguments; | |
304 | |
305 ConcatenateStrings(Continuation cont, List<Definition> args) | |
306 : continuation = new Reference(cont), | |
307 arguments = _referenceList(args); | |
308 | |
309 accept(Visitor visitor) => visitor.visitConcatenateStrings(this); | |
310 } | |
311 | |
312 /// Gets the value from a closure variable. The identity of the variable is | |
313 /// determined by a [Local]. | |
314 /// | |
315 /// Closure variables can be seen as ref cells that are not first-class values. | |
316 /// A [LetPrim] with a [GetClosureVariable] can then be seen as: | |
317 /// | |
318 /// let prim p = ![variable] in [body] | |
319 /// | |
320 class GetClosureVariable extends Primitive { | |
321 final Local variable; | |
322 | |
323 GetClosureVariable(this.variable) { | |
324 assert(variable != null); | |
325 } | |
326 | |
327 accept(Visitor visitor) => visitor.visitGetClosureVariable(this); | |
328 } | |
329 | |
330 /// Assign or declare a closure variable. The identity of the variable is | |
331 /// determined by a [Local]. | |
332 /// | |
333 /// Closure variables can be seen as ref cells that are not first-class values. | |
334 /// If [isDeclaration], this can seen as a let binding: | |
335 /// | |
336 /// let [variable] = ref [value] in [body] | |
337 /// | |
338 /// And otherwise, it can be seen as a dereferencing assignment: | |
339 /// | |
340 /// { ![variable] := [value]; [body] } | |
341 /// | |
342 /// Closure variables without a declaring [SetClosureVariable] are implicitly | |
343 /// declared at the entry to the [variable]'s enclosing function. | |
344 class SetClosureVariable extends Expression implements InteriorNode { | |
345 final Local variable; | |
346 final Reference value; | |
347 Expression body; | |
348 | |
349 /// If true, this declares a new copy of the closure variable. If so, all | |
350 /// uses of the closure variable must occur in the [body]. | |
351 /// | |
352 /// There can be at most one declaration per closure variable. If there is no | |
353 /// declaration, only one copy exists (per function execution). It is best to | |
354 /// avoid declaring closure variables if it is not necessary. | |
355 final bool isDeclaration; | |
356 | |
357 SetClosureVariable(this.variable, Primitive value, | |
358 {this.isDeclaration : false }) | |
359 : this.value = new Reference(value) { | |
360 assert(variable != null); | |
361 } | |
362 | |
363 accept(Visitor visitor) => visitor.visitSetClosureVariable(this); | |
364 | |
365 Expression plug(Expression expr) { | |
366 assert(body == null); | |
367 return body = expr; | |
368 } | |
369 } | |
370 | |
371 /// Create a potentially recursive function and store it in a closure variable. | |
372 /// The function can access itself using [GetClosureVariable] on [variable]. | |
373 /// There must not exist a [SetClosureVariable] to [variable]. | |
374 /// | |
375 /// This can be seen as a let rec binding: | |
376 /// | |
377 /// let rec [variable] = [definition] in [body] | |
378 /// | |
379 class DeclareFunction extends Expression implements InteriorNode { | |
380 final Local variable; | |
381 final FunctionDefinition definition; | |
382 Expression body; | |
383 | |
384 DeclareFunction(this.variable, this.definition); | |
385 | |
386 Expression plug(Expression expr) { | |
387 assert(body == null); | |
388 return body = expr; | |
389 } | |
390 | |
391 accept(Visitor visitor) => visitor.visitDeclareFunction(this); | |
392 } | |
393 | |
394 /// Invoke a continuation in tail position. | |
395 class InvokeContinuation extends Expression { | |
396 Reference continuation; | |
397 List<Reference> arguments; | |
398 | |
399 // An invocation of a continuation is recursive if it occurs in the body of | |
400 // the continuation itself. | |
401 bool isRecursive; | |
402 | |
403 InvokeContinuation(Continuation cont, List<Definition> args, | |
404 {recursive: false}) | |
405 : continuation = new Reference(cont), | |
406 arguments = _referenceList(args), | |
407 isRecursive = recursive { | |
408 assert(cont.parameters == null || | |
409 cont.parameters.length == args.length); | |
410 if (recursive) cont.isRecursive = true; | |
411 } | |
412 | |
413 /// A continuation invocation whose target and arguments will be filled | |
414 /// in later. | |
415 /// | |
416 /// Used as a placeholder for a jump whose target is not yet created | |
417 /// (e.g., in the translation of break and continue). | |
418 InvokeContinuation.uninitialized({recursive: false}) | |
419 : continuation = null, | |
420 arguments = null, | |
421 isRecursive = recursive; | |
422 | |
423 accept(Visitor visitor) => visitor.visitInvokeContinuation(this); | |
424 } | |
425 | |
426 /// The base class of things which can be tested and branched on. | |
427 abstract class Condition extends Node { | |
428 } | |
429 | |
430 class IsTrue extends Condition { | |
431 final Reference value; | |
432 | |
433 IsTrue(Definition val) : value = new Reference(val); | |
434 | |
435 accept(Visitor visitor) => visitor.visitIsTrue(this); | |
436 } | |
437 | |
438 /// Choose between a pair of continuations based on a condition value. | |
439 class Branch extends Expression { | |
440 final Condition condition; | |
441 final Reference trueContinuation; | |
442 final Reference falseContinuation; | |
443 | |
444 Branch(this.condition, Continuation trueCont, Continuation falseCont) | |
445 : trueContinuation = new Reference(trueCont), | |
446 falseContinuation = new Reference(falseCont); | |
447 | |
448 accept(Visitor visitor) => visitor.visitBranch(this); | |
449 } | |
450 | |
451 class Constant extends Primitive { | |
452 final ConstantExpression expression; | |
453 | |
454 Constant(this.expression); | |
455 | |
456 values.ConstantValue get value => expression.value; | |
457 | |
458 accept(Visitor visitor) => visitor.visitConstant(this); | |
459 } | |
460 | |
461 class This extends Primitive { | |
462 This(); | |
463 | |
464 accept(Visitor visitor) => visitor.visitThis(this); | |
465 } | |
466 | |
467 /// Reify the given type variable as a [Type]. | |
468 /// This depends on the current binding of 'this'. | |
469 class ReifyTypeVar extends Primitive { | |
470 final TypeVariableElement typeVariable; | |
471 | |
472 ReifyTypeVar(this.typeVariable); | |
473 | |
474 values.ConstantValue get constant => null; | |
475 | |
476 accept(Visitor visitor) => visitor.visitReifyTypeVar(this); | |
477 } | |
478 | |
479 class LiteralList extends Primitive { | |
480 /// The List type being created; this is not the type argument. | |
481 final GenericType type; | |
482 final List<Reference> values; | |
483 | |
484 LiteralList(this.type, List<Primitive> values) | |
485 : this.values = _referenceList(values); | |
486 | |
487 accept(Visitor visitor) => visitor.visitLiteralList(this); | |
488 } | |
489 | |
490 class LiteralMap extends Primitive { | |
491 final GenericType type; | |
492 final List<Reference> keys; | |
493 final List<Reference> values; | |
494 | |
495 LiteralMap(this.type, List<Primitive> keys, List<Primitive> values) | |
496 : this.keys = _referenceList(keys), | |
497 this.values = _referenceList(values); | |
498 | |
499 accept(Visitor visitor) => visitor.visitLiteralMap(this); | |
500 } | |
501 | |
502 /// Create a non-recursive function. | |
503 class CreateFunction extends Primitive { | |
504 final FunctionDefinition definition; | |
505 | |
506 CreateFunction(this.definition); | |
507 | |
508 accept(Visitor visitor) => visitor.visitCreateFunction(this); | |
509 } | |
510 | |
511 class Parameter extends Primitive { | |
512 Parameter(Element element) { | |
513 super.hint = element; | |
514 } | |
515 | |
516 accept(Visitor visitor) => visitor.visitParameter(this); | |
517 } | |
518 | |
519 /// Continuations are normally bound by 'let cont'. A continuation with one | |
520 /// parameter and no body is used to represent a function's return continuation. | |
521 /// The return continuation is bound by the Function, not by 'let cont'. | |
522 class Continuation extends Definition implements InteriorNode { | |
523 final List<Parameter> parameters; | |
524 Expression body = null; | |
525 | |
526 // A continuation is recursive if it has any recursive invocations. | |
527 bool isRecursive = false; | |
528 | |
529 bool get isReturnContinuation => body == null; | |
530 | |
531 Continuation(this.parameters); | |
532 | |
533 Continuation.retrn() : parameters = <Parameter>[new Parameter(null)]; | |
534 | |
535 accept(Visitor visitor) => visitor.visitContinuation(this); | |
536 } | |
537 | |
538 /// A function definition, consisting of parameters and a body. The parameters | |
539 /// include a distinguished continuation parameter. | |
540 class FunctionDefinition extends Node implements InteriorNode { | |
541 final FunctionElement element; | |
542 final Continuation returnContinuation; | |
543 final List<Parameter> parameters; | |
544 Expression body; | |
545 final List<ConstDeclaration> localConstants; | |
546 | |
547 /// Values for optional parameters. | |
548 final List<ConstantExpression> defaultParameterValues; | |
549 | |
550 FunctionDefinition(this.element, this.returnContinuation, | |
551 this.parameters, this.body, this.localConstants, | |
552 this.defaultParameterValues); | |
553 | |
554 FunctionDefinition.abstract(this.element, | |
555 this.parameters, | |
556 this.defaultParameterValues) | |
557 : this.returnContinuation = null, | |
558 this.localConstants = const <ConstDeclaration>[]; | |
559 | |
560 accept(Visitor visitor) => visitor.visitFunctionDefinition(this); | |
561 | |
562 /// Returns `true` if this function is abstract. | |
563 /// | |
564 /// If `true`, [body] and [returnContinuation] are `null` and [localConstants] | |
565 /// is empty. | |
566 bool get isAbstract => body == null; | |
567 } | |
568 | |
569 List<Reference> _referenceList(List<Definition> definitions) { | |
570 return definitions.map((e) => new Reference(e)).toList(); | |
571 } | |
572 | |
573 abstract class Visitor<T> { | |
574 T visit(Node node) => node.accept(this); | |
575 // Abstract classes. | |
576 T visitNode(Node node) => null; | |
577 T visitExpression(Expression node) => visitNode(node); | |
578 T visitDefinition(Definition node) => visitNode(node); | |
579 T visitPrimitive(Primitive node) => visitDefinition(node); | |
580 T visitCondition(Condition node) => visitNode(node); | |
581 | |
582 // Concrete classes. | |
583 T visitFunctionDefinition(FunctionDefinition node) => visitNode(node); | |
584 | |
585 // Expressions. | |
586 T visitLetPrim(LetPrim node) => visitExpression(node); | |
587 T visitLetCont(LetCont node) => visitExpression(node); | |
588 T visitInvokeStatic(InvokeStatic node) => visitExpression(node); | |
589 T visitInvokeContinuation(InvokeContinuation node) => visitExpression(node); | |
590 T visitInvokeMethod(InvokeMethod node) => visitExpression(node); | |
591 T visitInvokeSuperMethod(InvokeSuperMethod node) => visitExpression(node); | |
592 T visitInvokeConstructor(InvokeConstructor node) => visitExpression(node); | |
593 T visitConcatenateStrings(ConcatenateStrings node) => visitExpression(node); | |
594 T visitBranch(Branch node) => visitExpression(node); | |
595 T visitTypeOperator(TypeOperator node) => visitExpression(node); | |
596 T visitSetClosureVariable(SetClosureVariable node) => visitExpression(node); | |
597 T visitDeclareFunction(DeclareFunction node) => visitExpression(node); | |
598 | |
599 // Definitions. | |
600 T visitLiteralList(LiteralList node) => visitPrimitive(node); | |
601 T visitLiteralMap(LiteralMap node) => visitPrimitive(node); | |
602 T visitConstant(Constant node) => visitPrimitive(node); | |
603 T visitThis(This node) => visitPrimitive(node); | |
604 T visitReifyTypeVar(ReifyTypeVar node) => visitPrimitive(node); | |
605 T visitCreateFunction(CreateFunction node) => visitPrimitive(node); | |
606 T visitGetClosureVariable(GetClosureVariable node) => visitPrimitive(node); | |
607 T visitParameter(Parameter node) => visitPrimitive(node); | |
608 T visitContinuation(Continuation node) => visitDefinition(node); | |
609 | |
610 // Conditions. | |
611 T visitIsTrue(IsTrue node) => visitCondition(node); | |
612 } | |
613 | |
614 /// Recursively visits the entire CPS term, and calls abstract `process*` | |
615 /// (i.e. `processLetPrim`) functions in pre-order. | |
616 abstract class RecursiveVisitor extends Visitor { | |
617 // Ensures that RecursiveVisitor contains overrides for all relevant nodes. | |
618 // As a rule of thumb, nodes with structure to traverse should be overridden | |
619 // with the appropriate visits in this class (for example, visitLetCont), | |
620 // while leaving other nodes for subclasses (i.e., visitLiteralList). | |
621 visitNode(Node node) { | |
622 throw "RecursiveVisitor is stale, add missing visit overrides"; | |
623 } | |
624 | |
625 processReference(Reference ref) {} | |
626 | |
627 processFunctionDefinition(FunctionDefinition node) {} | |
628 visitFunctionDefinition(FunctionDefinition node) { | |
629 processFunctionDefinition(node); | |
630 node.parameters.forEach(visitParameter); | |
631 visit(node.body); | |
632 } | |
633 | |
634 // Expressions. | |
635 | |
636 processLetPrim(LetPrim node) {} | |
637 visitLetPrim(LetPrim node) { | |
638 processLetPrim(node); | |
639 visit(node.primitive); | |
640 visit(node.body); | |
641 } | |
642 | |
643 processLetCont(LetCont node) {} | |
644 visitLetCont(LetCont node) { | |
645 processLetCont(node); | |
646 visit(node.continuation); | |
647 visit(node.body); | |
648 } | |
649 | |
650 processInvokeStatic(InvokeStatic node) {} | |
651 visitInvokeStatic(InvokeStatic node) { | |
652 processInvokeStatic(node); | |
653 processReference(node.continuation); | |
654 node.arguments.forEach(processReference); | |
655 } | |
656 | |
657 processInvokeContinuation(InvokeContinuation node) {} | |
658 visitInvokeContinuation(InvokeContinuation node) { | |
659 processInvokeContinuation(node); | |
660 processReference(node.continuation); | |
661 node.arguments.forEach(processReference); | |
662 } | |
663 | |
664 processInvokeMethod(InvokeMethod node) {} | |
665 visitInvokeMethod(InvokeMethod node) { | |
666 processInvokeMethod(node); | |
667 processReference(node.receiver); | |
668 processReference(node.continuation); | |
669 node.arguments.forEach(processReference); | |
670 } | |
671 | |
672 processInvokeSuperMethod(InvokeSuperMethod node) {} | |
673 visitInvokeSuperMethod(InvokeSuperMethod node) { | |
674 processInvokeSuperMethod(node); | |
675 processReference(node.continuation); | |
676 node.arguments.forEach(processReference); | |
677 } | |
678 | |
679 processInvokeConstructor(InvokeConstructor node) {} | |
680 visitInvokeConstructor(InvokeConstructor node) { | |
681 processInvokeConstructor(node); | |
682 processReference(node.continuation); | |
683 node.arguments.forEach(processReference); | |
684 } | |
685 | |
686 processConcatenateStrings(ConcatenateStrings node) {} | |
687 visitConcatenateStrings(ConcatenateStrings node) { | |
688 processConcatenateStrings(node); | |
689 processReference(node.continuation); | |
690 node.arguments.forEach(processReference); | |
691 } | |
692 | |
693 | |
694 processBranch(Branch node) {} | |
695 visitBranch(Branch node) { | |
696 processBranch(node); | |
697 processReference(node.trueContinuation); | |
698 processReference(node.falseContinuation); | |
699 visit(node.condition); | |
700 } | |
701 | |
702 processTypeOperator(TypeOperator node) {} | |
703 visitTypeOperator(TypeOperator node) { | |
704 processTypeOperator(node); | |
705 processReference(node.continuation); | |
706 processReference(node.receiver); | |
707 } | |
708 | |
709 processSetClosureVariable(SetClosureVariable node) {} | |
710 visitSetClosureVariable(SetClosureVariable node) { | |
711 processSetClosureVariable(node); | |
712 processReference(node.value); | |
713 visit(node.body); | |
714 } | |
715 | |
716 processDeclareFunction(DeclareFunction node) {} | |
717 visitDeclareFunction(DeclareFunction node) { | |
718 processDeclareFunction(node); | |
719 visit(node.definition); | |
720 visit(node.body); | |
721 } | |
722 | |
723 // Definitions. | |
724 | |
725 processLiteralList(LiteralList node) {} | |
726 visitLiteralList(LiteralList node) { | |
727 processLiteralList(node); | |
728 node.values.forEach(processReference); | |
729 } | |
730 | |
731 processLiteralMap(LiteralMap node) {} | |
732 visitLiteralMap(LiteralMap node) { | |
733 processLiteralMap(node); | |
734 for (int i = 0; i < node.keys.length; i++) { | |
735 processReference(node.keys[i]); | |
736 processReference(node.values[i]); | |
737 } | |
738 } | |
739 | |
740 processConstant(Constant node) {} | |
741 visitConstant(Constant node) => processConstant(node); | |
742 | |
743 processThis(This node) {} | |
744 visitThis(This node) => processThis(node); | |
745 | |
746 processReifyTypeVar(ReifyTypeVar node) {} | |
747 visitReifyTypeVar(ReifyTypeVar node) => processReifyTypeVar(node); | |
748 | |
749 processCreateFunction(CreateFunction node) {} | |
750 visitCreateFunction(CreateFunction node) { | |
751 processCreateFunction(node); | |
752 visit(node.definition); | |
753 } | |
754 | |
755 processGetClosureVariable(GetClosureVariable node) {} | |
756 visitGetClosureVariable(GetClosureVariable node) => | |
757 processGetClosureVariable(node); | |
758 | |
759 processParameter(Parameter node) {} | |
760 visitParameter(Parameter node) => processParameter(node); | |
761 | |
762 processContinuation(Continuation node) {} | |
763 visitContinuation(Continuation node) { | |
764 processContinuation(node); | |
765 node.parameters.forEach(visitParameter); | |
766 visit(node.body); | |
767 } | |
768 | |
769 // Conditions. | |
770 | |
771 processIsTrue(IsTrue node) {} | |
772 visitIsTrue(IsTrue node) { | |
773 processIsTrue(node); | |
774 processReference(node.value); | |
775 } | |
776 } | |
777 | |
778 /// Keeps track of currently unused register indices. | |
779 class RegisterArray { | |
780 int nextIndex = 0; | |
781 final List<int> freeStack = <int>[]; | |
782 | |
783 /// Returns an index that is currently unused. | |
784 int makeIndex() { | |
785 if (freeStack.isEmpty) { | |
786 return nextIndex++; | |
787 } else { | |
788 return freeStack.removeLast(); | |
789 } | |
790 } | |
791 | |
792 void releaseIndex(int index) { | |
793 freeStack.add(index); | |
794 } | |
795 } | |
796 | |
797 /// Assigns indices to each primitive in the IR such that primitives that are | |
798 /// live simultaneously never get assigned the same index. | |
799 /// This information is used by the dart tree builder to generate fewer | |
800 /// redundant variables. | |
801 /// Currently, the liveness analysis is very simple and is often inadequate | |
802 /// for removing all of the redundant variables. | |
803 class RegisterAllocator extends Visitor { | |
804 /// Separate register spaces for each source-level variable/parameter. | |
805 /// Note that null is used as key for primitives without elements. | |
806 final Map<Element, RegisterArray> elementRegisters = | |
807 <Element, RegisterArray>{}; | |
808 | |
809 RegisterArray getRegisterArray(Element element) { | |
810 RegisterArray registers = elementRegisters[element]; | |
811 if (registers == null) { | |
812 registers = new RegisterArray(); | |
813 elementRegisters[element] = registers; | |
814 } | |
815 return registers; | |
816 } | |
817 | |
818 void allocate(Primitive primitive) { | |
819 if (primitive.registerIndex == null) { | |
820 primitive.registerIndex = getRegisterArray(primitive.hint).makeIndex(); | |
821 } | |
822 } | |
823 | |
824 void release(Primitive primitive) { | |
825 // Do not share indices for temporaries as this may obstruct inlining. | |
826 if (primitive.hint == null) return; | |
827 if (primitive.registerIndex != null) { | |
828 getRegisterArray(primitive.hint).releaseIndex(primitive.registerIndex); | |
829 } | |
830 } | |
831 | |
832 void visitReference(Reference reference) { | |
833 allocate(reference.definition); | |
834 } | |
835 | |
836 void visitFunctionDefinition(FunctionDefinition node) { | |
837 if (!node.isAbstract) { | |
838 visit(node.body); | |
839 } | |
840 node.parameters.forEach(allocate); // Assign indices to unused parameters. | |
841 elementRegisters.clear(); | |
842 } | |
843 | |
844 void visitLetPrim(LetPrim node) { | |
845 visit(node.body); | |
846 release(node.primitive); | |
847 visit(node.primitive); | |
848 } | |
849 | |
850 void visitLetCont(LetCont node) { | |
851 visit(node.continuation); | |
852 visit(node.body); | |
853 } | |
854 | |
855 void visitInvokeStatic(InvokeStatic node) { | |
856 node.arguments.forEach(visitReference); | |
857 } | |
858 | |
859 void visitInvokeContinuation(InvokeContinuation node) { | |
860 node.arguments.forEach(visitReference); | |
861 } | |
862 | |
863 void visitInvokeMethod(InvokeMethod node) { | |
864 visitReference(node.receiver); | |
865 node.arguments.forEach(visitReference); | |
866 } | |
867 | |
868 void visitInvokeSuperMethod(InvokeSuperMethod node) { | |
869 node.arguments.forEach(visitReference); | |
870 } | |
871 | |
872 void visitInvokeConstructor(InvokeConstructor node) { | |
873 node.arguments.forEach(visitReference); | |
874 } | |
875 | |
876 void visitConcatenateStrings(ConcatenateStrings node) { | |
877 node.arguments.forEach(visitReference); | |
878 } | |
879 | |
880 void visitBranch(Branch node) { | |
881 visit(node.condition); | |
882 } | |
883 | |
884 void visitLiteralList(LiteralList node) { | |
885 node.values.forEach(visitReference); | |
886 } | |
887 | |
888 void visitLiteralMap(LiteralMap node) { | |
889 for (int i = 0; i < node.keys.length; ++i) { | |
890 visitReference(node.keys[i]); | |
891 visitReference(node.values[i]); | |
892 } | |
893 } | |
894 | |
895 void visitTypeOperator(TypeOperator node) { | |
896 visitReference(node.receiver); | |
897 } | |
898 | |
899 void visitConstant(Constant node) { | |
900 } | |
901 | |
902 void visitThis(This node) { | |
903 } | |
904 | |
905 void visitReifyTypeVar(ReifyTypeVar node) { | |
906 } | |
907 | |
908 void visitCreateFunction(CreateFunction node) { | |
909 new RegisterAllocator().visit(node.definition); | |
910 } | |
911 | |
912 void visitGetClosureVariable(GetClosureVariable node) { | |
913 } | |
914 | |
915 void visitSetClosureVariable(SetClosureVariable node) { | |
916 visit(node.body); | |
917 visitReference(node.value); | |
918 } | |
919 | |
920 void visitDeclareFunction(DeclareFunction node) { | |
921 new RegisterAllocator().visit(node.definition); | |
922 visit(node.body); | |
923 } | |
924 | |
925 void visitParameter(Parameter node) { | |
926 throw "Parameters should not be visited by RegisterAllocator"; | |
927 } | |
928 | |
929 void visitContinuation(Continuation node) { | |
930 visit(node.body); | |
931 | |
932 // Arguments get allocated left-to-right, so we release parameters | |
933 // right-to-left. This increases the likelihood that arguments can be | |
934 // transferred without intermediate assignments. | |
935 for (int i = node.parameters.length - 1; i >= 0; --i) { | |
936 release(node.parameters[i]); | |
937 } | |
938 } | |
939 | |
940 void visitIsTrue(IsTrue node) { | |
941 visitReference(node.value); | |
942 } | |
943 | |
944 } | |
945 | |
OLD | NEW |