Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // IrNodes are kept in a separate library to have precise control over their | 5 // IrNodes are kept in a separate library to have precise control over their |
| 6 // dependencies on other parts of the system. | 6 // dependencies on other parts of the system. |
| 7 library dart2js.ir_nodes; | 7 library dart2js.ir_nodes; |
| 8 | 8 |
| 9 import '../dart2jslib.dart' as dart2js show Constant; | 9 import '../dart2jslib.dart' as dart2js show Constant; |
| 10 import '../elements/elements.dart' | 10 import '../elements/elements.dart' |
| 11 show FunctionElement, LibraryElement, ParameterElement, ClassElement; | 11 show FunctionElement, LibraryElement, ParameterElement, ClassElement, |
| 12 Element, VariableElement; | |
| 12 import '../universe/universe.dart' show Selector, SelectorKind; | 13 import '../universe/universe.dart' show Selector, SelectorKind; |
| 13 import '../dart_types.dart' show DartType, GenericType; | 14 import '../dart_types.dart' show DartType, GenericType; |
| 15 import '../helpers/helpers.dart'; | |
| 14 | 16 |
| 15 abstract class Node { | 17 abstract class Node { |
| 16 static int hashCount = 0; | 18 static int hashCount = 0; |
| 17 final int hashCode = hashCount = (hashCount + 1) & 0x3fffffff; | 19 final int hashCode = hashCount = (hashCount + 1) & 0x3fffffff; |
| 18 | 20 |
| 19 accept(Visitor visitor); | 21 accept(Visitor visitor); |
| 20 } | 22 } |
| 21 | 23 |
| 22 abstract class Expression extends Node { | 24 abstract class Expression extends Node { |
| 23 Expression plug(Expression expr) => throw 'impossible'; | 25 Expression plug(Expression expr) => throw 'impossible'; |
| 24 } | 26 } |
| 25 | 27 |
| 26 /// The base class of things that variables can refer to: primitives, | 28 /// The base class of things that variables can refer to: primitives, |
| 27 /// continuations, function and continuation parameters, etc. | 29 /// continuations, function and continuation parameters, etc. |
| 28 abstract class Definition extends Node { | 30 abstract class Definition extends Node { |
| 29 // The head of a linked-list of occurrences, in no particular order. | 31 // The head of a linked-list of occurrences, in no particular order. |
| 30 Reference firstRef = null; | 32 Reference firstRef = null; |
| 31 | 33 |
| 34 /// The [LetCont], [LetPrim], [Continuation] or [FunctionDefinition] binding | |
| 35 /// this definition. | |
| 36 Node binding; | |
| 37 | |
| 32 bool get hasAtMostOneUse => firstRef == null || firstRef.nextRef == null; | 38 bool get hasAtMostOneUse => firstRef == null || firstRef.nextRef == null; |
| 33 bool get hasExactlyOneUse => firstRef != null && firstRef.nextRef == null; | 39 bool get hasExactlyOneUse => firstRef != null && firstRef.nextRef == null; |
| 34 bool get hasAtLeastOneUse => firstRef != null; | 40 bool get hasAtLeastOneUse => firstRef != null; |
| 35 bool get hasMultipleUses => !hasAtMostOneUse; | 41 bool get hasMultipleUses => !hasAtMostOneUse; |
| 36 | 42 |
| 37 void substituteFor(Definition other) { | 43 void substituteFor(Definition other) { |
| 38 if (other.firstRef == null) return; | 44 if (other.firstRef == null) return; |
| 39 Reference previous, current = other.firstRef; | 45 Reference previous, current = other.firstRef; |
| 40 do { | 46 do { |
| 41 current.definition = this; | 47 current.definition = this; |
| 42 previous = current; | 48 previous = current; |
| 43 current = current.nextRef; | 49 current = current.nextRef; |
| 44 } while (current != null); | 50 } while (current != null); |
| 45 previous.nextRef = firstRef; | 51 previous.nextRef = firstRef; |
| 46 firstRef = other.firstRef; | 52 firstRef = other.firstRef; |
| 47 } | 53 } |
| 48 } | 54 } |
| 49 | 55 |
| 56 /// An pure expression that cannot throw or diverge. | |
|
sigurdm
2014/06/04 07:51:56
An -> A
| |
| 57 /// All primitives are named using the identity of the [Primitive] object. | |
| 50 abstract class Primitive extends Definition { | 58 abstract class Primitive extends Definition { |
| 59 /// The [VariableElement] or [ParameterElement] from which the primitive | |
| 60 /// binding originated. | |
| 61 Element element; | |
| 62 | |
| 63 /// Register in which the variable binding this primitive can be allocated. | |
| 64 /// Separate register spaces are used for primitives with different [element]. | |
| 65 /// Assigned by [RegisterAllocator], is null before that phase. | |
| 66 int registerIndex; | |
| 51 } | 67 } |
| 52 | 68 |
| 53 /// Operands to invocations and primitives are always variables. They point to | 69 /// Operands to invocations and primitives are always variables. They point to |
| 54 /// their definition and are linked into a list of occurrences. | 70 /// their definition and are linked into a list of occurrences. |
| 55 class Reference { | 71 class Reference { |
| 56 Definition definition; | 72 Definition definition; |
| 57 Reference nextRef = null; | 73 Reference nextRef = null; |
| 58 | 74 |
| 59 Reference(this.definition) { | 75 Reference(this.definition) { |
| 60 nextRef = definition.firstRef; | 76 nextRef = definition.firstRef; |
| 61 definition.firstRef = this; | 77 definition.firstRef = this; |
| 62 } | 78 } |
| 63 } | 79 } |
| 64 | 80 |
| 65 /// Binding a value (primitive or constant): 'let val x = V in E'. The bound | 81 /// Binding a value (primitive or constant): 'let val x = V in E'. The bound |
| 66 /// value is in scope in the body. | 82 /// value is in scope in the body. |
| 67 /// During one-pass construction a LetVal with an empty body is used to | 83 /// During one-pass construction a LetVal with an empty body is used to |
| 68 /// represent one-level context 'let val x = V in []'. | 84 /// represent one-level context 'let val x = V in []'. |
| 69 class LetPrim extends Expression { | 85 class LetPrim extends Expression { |
| 70 final Primitive primitive; | 86 final Primitive primitive; |
| 71 Expression body = null; | 87 Expression body = null; |
| 72 | 88 |
| 73 LetPrim(this.primitive); | 89 LetPrim(this.primitive) { |
| 90 primitive.binding = this; | |
| 91 } | |
| 74 | 92 |
| 75 Expression plug(Expression expr) { | 93 Expression plug(Expression expr) { |
| 76 assert(body == null); | 94 assert(body == null); |
| 77 return body = expr; | 95 return body = expr; |
| 78 } | 96 } |
| 79 | 97 |
| 80 accept(Visitor visitor) => visitor.visitLetPrim(this); | 98 accept(Visitor visitor) => visitor.visitLetPrim(this); |
| 81 } | 99 } |
| 82 | 100 |
| 83 | 101 |
| 84 /// Binding a continuation: 'let cont k(v) = E in E'. The bound continuation | 102 /// Binding a continuation: 'let cont k(v) = E in E'. The bound continuation |
| 85 /// is in scope in the body and the continuation parameter is in scope in the | 103 /// is in scope in the body and the continuation parameter is in scope in the |
| 86 /// continuation body. | 104 /// continuation body. |
| 87 /// During one-pass construction a LetCont with an empty continuation body is | 105 /// During one-pass construction a LetCont with an empty continuation body is |
| 88 /// used to represent the one-level context 'let cont k(v) = [] in E'. | 106 /// used to represent the one-level context 'let cont k(v) = [] in E'. |
| 89 class LetCont extends Expression { | 107 class LetCont extends Expression { |
| 90 final Continuation continuation; | 108 final Continuation continuation; |
| 91 final Expression body; | 109 final Expression body; |
| 92 | 110 |
| 93 LetCont(this.continuation, this.body); | 111 LetCont(this.continuation, this.body) { |
| 112 continuation.binding = this; | |
| 113 } | |
| 94 | 114 |
| 95 Expression plug(Expression expr) { | 115 Expression plug(Expression expr) { |
| 96 assert(continuation.body == null); | 116 assert(continuation.body == null); |
| 97 return continuation.body = expr; | 117 return continuation.body = expr; |
| 98 } | 118 } |
| 99 | 119 |
| 100 accept(Visitor visitor) => visitor.visitLetCont(this); | 120 accept(Visitor visitor) => visitor.visitLetCont(this); |
| 101 } | 121 } |
| 102 | 122 |
| 103 abstract class Invoke { | 123 abstract class Invoke { |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 266 List<Reference> values; | 286 List<Reference> values; |
| 267 | 287 |
| 268 LiteralMap(List<Primitive> keys, List<Primitive> values) | 288 LiteralMap(List<Primitive> keys, List<Primitive> values) |
| 269 : this.keys = _referenceList(keys), | 289 : this.keys = _referenceList(keys), |
| 270 this.values = _referenceList(values); | 290 this.values = _referenceList(values); |
| 271 | 291 |
| 272 accept(Visitor visitor) => visitor.visitLiteralMap(this); | 292 accept(Visitor visitor) => visitor.visitLiteralMap(this); |
| 273 } | 293 } |
| 274 | 294 |
| 275 class Parameter extends Primitive { | 295 class Parameter extends Primitive { |
| 276 final ParameterElement element; | 296 Parameter(Element element) { |
| 277 | 297 super.element = element; |
| 278 Parameter(this.element); | 298 } |
| 279 | 299 |
| 280 accept(Visitor visitor) => visitor.visitParameter(this); | 300 accept(Visitor visitor) => visitor.visitParameter(this); |
| 281 } | 301 } |
| 282 | 302 |
| 283 /// Continuations are normally bound by 'let cont'. A continuation with no | 303 /// Continuations are normally bound by 'let cont'. A continuation with no |
| 284 /// parameter (or body) is used to represent a function's return continuation. | 304 /// parameter (or body) is used to represent a function's return continuation. |
| 285 /// The return continuation is bound by the Function, not by 'let cont'. | 305 /// The return continuation is bound by the Function, not by 'let cont'. |
| 286 class Continuation extends Definition { | 306 class Continuation extends Definition { |
| 287 final List<Parameter> parameters; | 307 final List<Parameter> parameters; |
| 288 Expression body = null; | 308 Expression body = null; |
| 289 | 309 |
| 290 // A continuation is recursive if it has any recursive invocations. | 310 // A continuation is recursive if it has any recursive invocations. |
| 291 bool isRecursive = false; | 311 bool isRecursive = false; |
| 292 | 312 |
| 293 Continuation(this.parameters); | 313 Continuation(this.parameters) { |
| 314 for (Parameter param in parameters) { | |
| 315 param.binding = this; | |
| 316 } | |
| 317 } | |
| 294 | 318 |
| 295 Continuation.retrn() : parameters = null; | 319 Continuation.retrn() : parameters = null; |
| 296 | 320 |
| 297 accept(Visitor visitor) => visitor.visitContinuation(this); | 321 accept(Visitor visitor) => visitor.visitContinuation(this); |
| 298 } | 322 } |
| 299 | 323 |
| 300 /// A function definition, consisting of parameters and a body. The parameters | 324 /// A function definition, consisting of parameters and a body. The parameters |
| 301 /// include a distinguished continuation parameter. | 325 /// include a distinguished continuation parameter. |
| 302 class FunctionDefinition extends Node { | 326 class FunctionDefinition extends Node { |
| 303 final Continuation returnContinuation; | 327 final Continuation returnContinuation; |
| 304 final List<Parameter> parameters; | 328 final List<Parameter> parameters; |
| 305 final Expression body; | 329 final Expression body; |
| 306 | 330 |
| 307 FunctionDefinition(this.returnContinuation, this.parameters, this.body); | 331 FunctionDefinition(this.returnContinuation, this.parameters, this.body) { |
| 332 for (Parameter param in parameters) { | |
| 333 param.binding = this; | |
| 334 } | |
| 335 returnContinuation.binding = this; | |
| 336 } | |
| 308 | 337 |
| 309 accept(Visitor visitor) => visitor.visitFunctionDefinition(this); | 338 accept(Visitor visitor) => visitor.visitFunctionDefinition(this); |
| 310 } | 339 } |
| 311 | 340 |
| 312 List<Reference> _referenceList(List<Definition> definitions) { | 341 List<Reference> _referenceList(List<Definition> definitions) { |
| 313 return definitions.map((e) => new Reference(e)).toList(growable: false); | 342 return definitions.map((e) => new Reference(e)).toList(growable: false); |
| 314 } | 343 } |
| 315 | 344 |
| 316 abstract class Visitor<T> { | 345 abstract class Visitor<T> { |
| 317 T visit(Node node) => node.accept(this); | 346 T visit(Node node) => node.accept(this); |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 339 T visitLiteralList(LiteralList node) => visitPrimitive(node); | 368 T visitLiteralList(LiteralList node) => visitPrimitive(node); |
| 340 T visitLiteralMap(LiteralMap node) => visitPrimitive(node); | 369 T visitLiteralMap(LiteralMap node) => visitPrimitive(node); |
| 341 T visitConstant(Constant node) => visitPrimitive(node); | 370 T visitConstant(Constant node) => visitPrimitive(node); |
| 342 T visitParameter(Parameter node) => visitPrimitive(node); | 371 T visitParameter(Parameter node) => visitPrimitive(node); |
| 343 T visitContinuation(Continuation node) => visitDefinition(node); | 372 T visitContinuation(Continuation node) => visitDefinition(node); |
| 344 | 373 |
| 345 // Conditions. | 374 // Conditions. |
| 346 T visitIsTrue(IsTrue node) => visitCondition(node); | 375 T visitIsTrue(IsTrue node) => visitCondition(node); |
| 347 } | 376 } |
| 348 | 377 |
| 378 | |
|
sigurdm
2014/06/04 07:51:56
Extra newline
| |
| 349 /// Generate a Lisp-like S-expression representation of an IR node as a string. | 379 /// Generate a Lisp-like S-expression representation of an IR node as a string. |
| 350 /// The representation is not pretty-printed, but it can easily be quoted and | 380 /// The representation is not pretty-printed, but it can easily be quoted and |
| 351 /// dropped into the REPL of one's favorite Lisp or Scheme implementation to be | 381 /// dropped into the REPL of one's favorite Lisp or Scheme implementation to be |
| 352 /// pretty-printed. | 382 /// pretty-printed. |
| 353 class SExpressionStringifier extends Visitor<String> { | 383 class SExpressionStringifier extends Visitor<String> { |
| 354 final Map<Definition, String> names = <Definition, String>{}; | 384 final Map<Definition, String> names = <Definition, String>{}; |
| 355 | 385 |
| 356 int _valueCounter = 0; | 386 int _valueCounter = 0; |
| 357 int _continuationCounter = 0; | 387 int _continuationCounter = 0; |
| 358 | 388 |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 468 String visitContinuation(Continuation node) { | 498 String visitContinuation(Continuation node) { |
| 469 // Continuations are visited directly in visitLetCont. | 499 // Continuations are visited directly in visitLetCont. |
| 470 return '(Unexpected Continuation)'; | 500 return '(Unexpected Continuation)'; |
| 471 } | 501 } |
| 472 | 502 |
| 473 String visitIsTrue(IsTrue node) { | 503 String visitIsTrue(IsTrue node) { |
| 474 String value = names[node.value.definition]; | 504 String value = names[node.value.definition]; |
| 475 return '(IsTrue $value)'; | 505 return '(IsTrue $value)'; |
| 476 } | 506 } |
| 477 } | 507 } |
| 508 | |
| 509 /// Determines for each continuations the highest scope to which it can be | |
| 510 /// lifted without moving a reference out of scope. | |
| 511 /// A scope is a [LetPrim], [Continuation], or top-level (represented as null). | |
| 512 /// Does not mutate the IR. | |
| 513 class FindLiftableContinuations extends Visitor { | |
| 514 final Map<Node, int> scope2depth = <Node, int>{}; | |
| 515 | |
| 516 /// Maps a scope to its enclosing continuation (prior to lifting) or null | |
| 517 /// if not inside a continuation. | |
| 518 final Map<Node, Continuation> enclosingContinuation = <Node, Continuation>{}; | |
| 519 | |
| 520 /// Maps a continuation to the outermost scope to which it can be lifted, | |
| 521 /// or null or absent if it can be lifted all the way to top-level. | |
| 522 final Map<Continuation, Node> liftTarget = <Continuation, Node>{}; | |
| 523 | |
| 524 /// Inverse of [liftTarget]. | |
| 525 final Map<Node, List<Continuation>> liftedContinuations = | |
| 526 <Node, List<Continuation>>{}; | |
| 527 | |
| 528 Continuation currentContinuation; | |
| 529 int currentDepth = 0; | |
| 530 | |
| 531 /// Restricts [child] so that it cannot be lifted outside of [ancestor]. | |
| 532 void requireAncestor(Node child, Node ancestor) { | |
| 533 // Only continuations are lifted, but in doing so, all let bindings inside | |
| 534 // the continuation are also lifted with it. we therefore restrict the | |
| 535 // lifting of the continuation that encloses [child]. | |
| 536 Continuation cont = enclosingContinuation[child]; | |
| 537 | |
| 538 // If [child] is in the top-level it cannot be lifted, hence no restrictions | |
| 539 // are necessary | |
| 540 if (cont == null) return; | |
| 541 | |
| 542 // If [child] and [ancestor] are in the same continuation, their nesting | |
| 543 // order is unaffected by lifting, so no restrictions are necessary. | |
| 544 if (enclosingContinuation[ancestor] == cont) return; | |
| 545 | |
| 546 // The continuation may not be lifted further than [ancestor]. | |
| 547 liftTarget[cont] = join(liftTarget[cont], ancestor); | |
| 548 | |
| 549 assert(liftTarget[cont] != cont); | |
| 550 } | |
| 551 | |
| 552 /// Returns the deepest of the two given scopes. The nesting order of scopes | |
| 553 /// may be affected by lifting, but this function will restrict lifting of | |
| 554 /// continuations to ensure that the returned scope remains the most deeply | |
| 555 /// nested scope also after lifting. | |
| 556 Node join(Node s1, Node s2) { | |
| 557 if (s1 == null) return s2; | |
| 558 if (s2 == null) return s1; | |
| 559 if (s1 == s2) return s1; | |
| 560 int d1 = scope2depth[s1]; | |
| 561 int d2 = scope2depth[s2]; | |
| 562 assert(d1 != d2); | |
| 563 if (d1 < d2) { // s1 is ancestor of s2? | |
| 564 requireAncestor(s2, s1); // ensure that s2 remains inside of s1 | |
| 565 return s2; | |
| 566 } else { | |
| 567 requireAncestor(s1, s2); // ensure that s1 remains inside of s2 | |
| 568 return s1; | |
| 569 } | |
| 570 } | |
| 571 | |
| 572 /// Returns the continuations that could be lifted to the given scope. | |
| 573 Iterable<Continuation> getContinuationsLiftedTo(Node scope) { | |
| 574 List list = liftedContinuations[scope]; | |
| 575 if (list != null) | |
| 576 return list; | |
| 577 return const []; | |
| 578 } | |
| 579 | |
| 580 void visitFunctionDefinition(FunctionDefinition node) { | |
| 581 scope2depth[node] = 0; | |
| 582 ++currentDepth; | |
| 583 visit(node.body); | |
| 584 --currentDepth; | |
| 585 | |
| 586 // Build inverse of liftTarget | |
| 587 for (Continuation cont in liftTarget.keys) { | |
| 588 Node target = liftTarget[cont]; | |
| 589 List<Continuation> list = liftedContinuations[target]; | |
| 590 if (list == null) { | |
| 591 list = <Continuation>[]; | |
| 592 liftedContinuations[target] = list; | |
| 593 } | |
| 594 list.add(cont); | |
| 595 } | |
| 596 } | |
| 597 | |
| 598 // visit returns the outermost scope to which the given expression | |
| 599 // can be lifted without breaking scope. | |
| 600 | |
| 601 Node visitReference(Reference ref) { | |
| 602 Definition definition = ref.definition; | |
| 603 if (definition is Continuation) { | |
| 604 // Non-recursive reference to a continuation. Its lift target has been | |
| 605 // completely resolved by now since we are inside the LetCont binding. | |
| 606 // This reference can be lifted as far as the continuation. | |
| 607 return liftTarget[definition]; | |
| 608 } else { | |
| 609 return ref.definition.binding; | |
|
sigurdm
2014/06/04 07:51:56
You can use
return definition.binding
asgerf
2014/06/04 09:40:51
Thanks
| |
| 610 } | |
| 611 } | |
| 612 | |
| 613 Node visitReferenceList(List<Reference> refs) { | |
|
sigurdm
2014/06/04 07:51:56
Could be a fold
| |
| 614 Node scope = null; | |
| 615 for (Reference ref in refs) { | |
| 616 scope = join(scope, visitReference(ref)); | |
| 617 } | |
| 618 return scope; | |
| 619 } | |
| 620 | |
| 621 Node visitRecursiveReference(Reference ref) { | |
| 622 // A recursive self-reference inside a continuation can be lifted to the | |
| 623 // body of the continuation, not the body of the LetCont. | |
| 624 return ref.definition as Continuation; | |
| 625 } | |
| 626 | |
| 627 Node visitLetPrim(LetPrim node) { | |
| 628 Node primScope = visit(node.primitive); | |
| 629 scope2depth[node] = currentDepth; | |
| 630 enclosingContinuation[node] = currentContinuation; | |
| 631 ++currentDepth; | |
| 632 Node bodyScope = visit(node.body); | |
| 633 --currentDepth; | |
| 634 return join(primScope, bodyScope); | |
| 635 } | |
| 636 | |
| 637 Node visitLetCont(LetCont node) { | |
| 638 scope2depth[node] = currentDepth; | |
| 639 enclosingContinuation[node] = currentContinuation; | |
| 640 ++currentDepth; | |
| 641 visit(node.continuation); | |
| 642 Node bodyScope = visit(node.body); | |
| 643 --currentDepth; | |
| 644 return bodyScope; | |
| 645 } | |
| 646 | |
| 647 Node visitInvokeStatic(InvokeStatic node) { | |
| 648 Node argScope = visitReferenceList(node.arguments); | |
| 649 Node contScope = visitReference(node.continuation); | |
| 650 return join(argScope, contScope); | |
| 651 } | |
| 652 | |
| 653 Node visitInvokeContinuation(InvokeContinuation node) { | |
| 654 Node argScope = visitReferenceList(node.arguments); | |
| 655 Node contScope = node.isRecursive | |
| 656 ? visitRecursiveReference(node.continuation) | |
| 657 : visitReference(node.continuation); | |
| 658 return join(argScope, contScope); | |
| 659 } | |
| 660 | |
| 661 Node visitInvokeMethod(InvokeMethod node) { | |
| 662 Node receiverScope = visitReference(node.receiver); | |
| 663 Node argScope = visitReferenceList(node.arguments); | |
| 664 Node contScope = visitReference(node.continuation); | |
| 665 return join(receiverScope, join(argScope, contScope)); | |
| 666 } | |
| 667 | |
| 668 Node visitInvokeConstructor(InvokeConstructor node) { | |
| 669 Node argScope = visitReferenceList(node.arguments); | |
| 670 Node contScope = visitReference(node.continuation); | |
| 671 return join(argScope, contScope); | |
| 672 } | |
| 673 | |
| 674 Node visitConcatenateStrings(ConcatenateStrings node) { | |
| 675 Node argScope = visitReferenceList(node.arguments); | |
| 676 Node contScope = visitReference(node.continuation); | |
| 677 return join(argScope, contScope); | |
| 678 } | |
| 679 | |
| 680 Node visitBranch(Branch node) { | |
| 681 Node condScope = visit(node.condition); | |
| 682 Node thenScope = visitReference(node.trueContinuation); | |
| 683 Node elseScope = visitReference(node.falseContinuation); | |
| 684 return join(condScope, join(thenScope, elseScope)); | |
| 685 } | |
| 686 | |
| 687 Node visitLiteralList(LiteralList node) { | |
| 688 return visitReferenceList(node.values); | |
| 689 } | |
| 690 | |
| 691 Node visitLiteralMap(LiteralMap node) { | |
| 692 Node keyScope = visitReferenceList(node.keys); | |
| 693 Node valueScope = visitReferenceList(node.values); | |
| 694 return join(keyScope, valueScope); | |
| 695 } | |
| 696 | |
| 697 Node visitConstant(Constant node) { | |
| 698 return null; | |
| 699 } | |
| 700 | |
| 701 Node visitParameter(Parameter node) { | |
| 702 throw "Parameters should not be visited"; | |
| 703 } | |
| 704 | |
| 705 void visitContinuation(Continuation node) { | |
| 706 enclosingContinuation[node] = node; | |
| 707 liftTarget[node] = null; | |
| 708 scope2depth[node] = currentDepth; | |
| 709 Continuation oldCont = currentContinuation; | |
| 710 currentContinuation = node; | |
| 711 ++currentDepth; | |
| 712 Node bodyScope = visit(node.body); | |
| 713 --currentDepth; | |
| 714 // If the body contains a reference to one of the continuation parameters, | |
| 715 // then the scope will be the continuation itself, and the nesting | |
| 716 // restrictions on the continuation will have been added by join(). | |
| 717 // In other cases we must add the nesting restrictions here. | |
| 718 if (bodyScope != node) { | |
| 719 requireAncestor(node, bodyScope); | |
| 720 } | |
| 721 currentContinuation = oldCont; | |
| 722 } | |
| 723 | |
| 724 Node visitIsTrue(IsTrue node) { | |
| 725 return visitReference(node.value); | |
| 726 } | |
| 727 | |
| 728 } | |
| 729 | |
| 730 /// Keeps track of currently unused register indices. | |
| 731 class RegisterArray { | |
| 732 int nextIndex = 0; | |
| 733 final List<int> freeStack = <int>[]; | |
| 734 | |
| 735 int makeIndex() { | |
| 736 if (freeStack.isEmpty) { | |
| 737 return nextIndex++; | |
| 738 } else { | |
| 739 return freeStack.removeLast(); | |
| 740 } | |
| 741 } | |
| 742 | |
| 743 void releaseIndex(int index) { | |
| 744 freeStack.add(index); | |
| 745 } | |
| 746 } | |
| 747 | |
| 748 /// Assigns indices to each primitive in the IR such that primitives that are | |
| 749 /// live simultaneously never get assigned the same index. | |
| 750 /// This information is used by the dart tree builder to generate fewer | |
| 751 /// redundant variables. | |
| 752 /// Currently, the liveness analysis is very simple and is often inadequate | |
| 753 /// for removing all of the redundant variables. | |
| 754 class RegisterAllocator extends Visitor { | |
| 755 final FindLiftableContinuations lifts = new FindLiftableContinuations(); | |
| 756 | |
| 757 /// Separate register spaces for each source-level variable/parameter. | |
| 758 /// Note that null is used as key for primitives without elements. | |
| 759 final Map<Element, RegisterArray> elementRegisters = | |
| 760 <Element, RegisterArray>{}; | |
| 761 | |
| 762 RegisterArray getRegisterArray(Element element) { | |
| 763 RegisterArray registers = elementRegisters[element]; | |
| 764 if (registers == null) { | |
| 765 registers = new RegisterArray(); | |
| 766 elementRegisters[element] = registers; | |
| 767 } | |
| 768 return registers; | |
| 769 } | |
| 770 | |
| 771 void allocate(Primitive primitive) { | |
| 772 if (primitive.registerIndex == null) { | |
| 773 primitive.registerIndex = getRegisterArray(primitive.element).makeIndex(); | |
| 774 } | |
| 775 } | |
| 776 | |
| 777 void release(Primitive primitive) { | |
| 778 // Do not share indices for temporaries as this may obstruct inlining. | |
| 779 if (primitive.element == null) return; | |
| 780 if (primitive.registerIndex != null) { | |
| 781 getRegisterArray(primitive.element).releaseIndex(primitive.registerIndex); | |
| 782 } | |
| 783 } | |
| 784 | |
| 785 void visitLiftedContinuations(Node scope) { | |
| 786 for (Continuation cont in lifts.getContinuationsLiftedTo(scope)) { | |
| 787 assert(cont != scope); | |
| 788 visit(cont); | |
| 789 } | |
| 790 } | |
| 791 | |
| 792 void visitReference(Reference reference) { | |
| 793 allocate(reference.definition); | |
| 794 } | |
| 795 | |
| 796 void visitFunctionDefinition(FunctionDefinition node) { | |
| 797 lifts.visit(node); | |
| 798 visitLiftedContinuations(node); | |
| 799 visitLiftedContinuations(null); | |
| 800 visit(node.body); | |
| 801 node.parameters.forEach(allocate); // Assign indices to unused parameters. | |
| 802 elementRegisters.clear(); | |
| 803 } | |
| 804 | |
| 805 void visitLetPrim(LetPrim node) { | |
| 806 visitLiftedContinuations(node); | |
| 807 visit(node.body); | |
| 808 release(node.primitive); | |
| 809 visit(node.primitive); | |
| 810 } | |
| 811 | |
| 812 void visitLetCont(LetCont node) { | |
| 813 assert(lifts.liftTarget.containsKey(node.continuation)); | |
| 814 visit(node.body); | |
| 815 } | |
| 816 | |
| 817 void visitInvokeStatic(InvokeStatic node) { | |
| 818 node.arguments.forEach(visitReference); | |
| 819 } | |
| 820 | |
| 821 void visitInvokeContinuation(InvokeContinuation node) { | |
| 822 node.arguments.forEach(visitReference); | |
| 823 } | |
| 824 | |
| 825 void visitInvokeMethod(InvokeMethod node) { | |
| 826 visitReference(node.receiver); | |
| 827 node.arguments.forEach(visitReference); | |
| 828 } | |
| 829 | |
| 830 void visitInvokeConstructor(InvokeConstructor node) { | |
| 831 node.arguments.forEach(visitReference); | |
| 832 } | |
| 833 | |
| 834 void visitConcatenateStrings(ConcatenateStrings node) { | |
| 835 node.arguments.forEach(visitReference); | |
| 836 } | |
| 837 | |
| 838 void visitBranch(Branch node) { | |
| 839 visit(node.condition); | |
| 840 } | |
| 841 | |
| 842 void visitLiteralList(LiteralList node) { | |
| 843 node.values.forEach(visitReference); | |
| 844 } | |
| 845 | |
| 846 void visitLiteralMap(LiteralMap node) { | |
| 847 for (int i = 0; i < node.keys.length; ++i) { | |
| 848 visitReference(node.keys[i]); | |
| 849 visitReference(node.values[i]); | |
| 850 } | |
| 851 } | |
| 852 | |
| 853 void visitConstant(Constant node) { | |
| 854 } | |
| 855 | |
| 856 void visitParameter(Parameter node) { | |
| 857 throw "Parameters should not be visited by RegisterAllocator"; | |
| 858 } | |
| 859 | |
| 860 void visitContinuation(Continuation node) { | |
| 861 visitLiftedContinuations(node); | |
| 862 visit(node.body); | |
| 863 | |
| 864 // Arguments get allocated left-to-right, so we release parameters | |
| 865 // right-to-left. This increases the likelihood that arguments can be | |
| 866 // transferred without intermediate assignments. | |
| 867 for (int i = node.parameters.length - 1; i >= 0; --i) { | |
| 868 release(node.parameters[i]); | |
| 869 } | |
| 870 } | |
| 871 | |
| 872 void visitIsTrue(IsTrue node) { | |
| 873 visitReference(node.value); | |
| 874 } | |
| 875 | |
| 876 } | |
| OLD | NEW |