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 /// A pure expression that cannot throw or diverge. | |
| 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 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 291 List<Reference> values; | 311 List<Reference> values; |
| 292 | 312 |
| 293 LiteralMap(List<Primitive> keys, List<Primitive> values) | 313 LiteralMap(List<Primitive> keys, List<Primitive> values) |
| 294 : this.keys = _referenceList(keys), | 314 : this.keys = _referenceList(keys), |
| 295 this.values = _referenceList(values); | 315 this.values = _referenceList(values); |
| 296 | 316 |
| 297 accept(Visitor visitor) => visitor.visitLiteralMap(this); | 317 accept(Visitor visitor) => visitor.visitLiteralMap(this); |
| 298 } | 318 } |
| 299 | 319 |
| 300 class Parameter extends Primitive { | 320 class Parameter extends Primitive { |
| 301 final ParameterElement element; | 321 Parameter(Element element) { |
| 302 | 322 super.element = element; |
| 303 Parameter(this.element); | 323 } |
| 304 | 324 |
| 305 accept(Visitor visitor) => visitor.visitParameter(this); | 325 accept(Visitor visitor) => visitor.visitParameter(this); |
| 306 } | 326 } |
| 307 | 327 |
| 308 /// Continuations are normally bound by 'let cont'. A continuation with no | 328 /// Continuations are normally bound by 'let cont'. A continuation with no |
| 309 /// parameter (or body) is used to represent a function's return continuation. | 329 /// parameter (or body) is used to represent a function's return continuation. |
| 310 /// The return continuation is bound by the Function, not by 'let cont'. | 330 /// The return continuation is bound by the Function, not by 'let cont'. |
| 311 class Continuation extends Definition { | 331 class Continuation extends Definition { |
| 312 final List<Parameter> parameters; | 332 final List<Parameter> parameters; |
| 313 Expression body = null; | 333 Expression body = null; |
| 314 | 334 |
| 315 // A continuation is recursive if it has any recursive invocations. | 335 // A continuation is recursive if it has any recursive invocations. |
| 316 bool isRecursive = false; | 336 bool isRecursive = false; |
| 317 | 337 |
| 318 Continuation(this.parameters); | 338 Continuation(this.parameters) { |
| 339 for (Parameter param in parameters) { | |
| 340 param.binding = this; | |
| 341 } | |
| 342 } | |
| 319 | 343 |
| 320 Continuation.retrn() : parameters = null; | 344 Continuation.retrn() : parameters = null; |
| 321 | 345 |
| 322 accept(Visitor visitor) => visitor.visitContinuation(this); | 346 accept(Visitor visitor) => visitor.visitContinuation(this); |
| 323 } | 347 } |
| 324 | 348 |
| 325 /// A function definition, consisting of parameters and a body. The parameters | 349 /// A function definition, consisting of parameters and a body. The parameters |
| 326 /// include a distinguished continuation parameter. | 350 /// include a distinguished continuation parameter. |
| 327 class FunctionDefinition extends Node { | 351 class FunctionDefinition extends Node { |
| 328 final Continuation returnContinuation; | 352 final Continuation returnContinuation; |
| 329 final List<Parameter> parameters; | 353 final List<Parameter> parameters; |
| 330 final Expression body; | 354 final Expression body; |
| 331 | 355 |
| 332 FunctionDefinition(this.returnContinuation, this.parameters, this.body); | 356 FunctionDefinition(this.returnContinuation, this.parameters, this.body) { |
| 357 for (Parameter param in parameters) { | |
| 358 param.binding = this; | |
| 359 } | |
| 360 returnContinuation.binding = this; | |
| 361 } | |
| 333 | 362 |
| 334 accept(Visitor visitor) => visitor.visitFunctionDefinition(this); | 363 accept(Visitor visitor) => visitor.visitFunctionDefinition(this); |
| 335 } | 364 } |
| 336 | 365 |
| 337 List<Reference> _referenceList(List<Definition> definitions) { | 366 List<Reference> _referenceList(List<Definition> definitions) { |
| 338 return definitions.map((e) => new Reference(e)).toList(growable: false); | 367 return definitions.map((e) => new Reference(e)).toList(growable: false); |
| 339 } | 368 } |
| 340 | 369 |
| 341 abstract class Visitor<T> { | 370 abstract class Visitor<T> { |
| 342 T visit(Node node) => node.accept(this); | 371 T visit(Node node) => node.accept(this); |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 494 String visitContinuation(Continuation node) { | 523 String visitContinuation(Continuation node) { |
| 495 // Continuations are visited directly in visitLetCont. | 524 // Continuations are visited directly in visitLetCont. |
| 496 return '(Unexpected Continuation)'; | 525 return '(Unexpected Continuation)'; |
| 497 } | 526 } |
| 498 | 527 |
| 499 String visitIsTrue(IsTrue node) { | 528 String visitIsTrue(IsTrue node) { |
| 500 String value = names[node.value.definition]; | 529 String value = names[node.value.definition]; |
| 501 return '(IsTrue $value)'; | 530 return '(IsTrue $value)'; |
| 502 } | 531 } |
| 503 } | 532 } |
| 533 | |
| 534 /// Determines for each continuations the highest scope to which it can be | |
| 535 /// lifted without moving a reference out of scope. | |
| 536 /// A scope is a [LetPrim], [Continuation], or top-level (represented as null). | |
| 537 /// Does not mutate the IR. | |
| 538 class FindLiftableContinuations extends Visitor { | |
| 539 final Map<Node, int> scope2depth = <Node, int>{}; | |
| 540 | |
| 541 /// Maps a scope to its enclosing continuation (prior to lifting) or null | |
| 542 /// if not inside a continuation. | |
| 543 final Map<Node, Continuation> enclosingContinuation = <Node, Continuation>{}; | |
| 544 | |
| 545 /// Maps a continuation to the outermost scope to which it can be lifted, | |
| 546 /// or null or absent if it can be lifted all the way to top-level. | |
| 547 final Map<Continuation, Node> liftTarget = <Continuation, Node>{}; | |
| 548 | |
| 549 /// Inverse of [liftTarget]. | |
| 550 final Map<Node, List<Continuation>> liftedContinuations = | |
| 551 <Node, List<Continuation>>{}; | |
| 552 | |
| 553 Continuation currentContinuation; | |
| 554 int currentDepth = 0; | |
| 555 | |
| 556 /// Restricts [child] so that it cannot be lifted outside of [ancestor]. | |
| 557 void requireAncestor(Node child, Node ancestor) { | |
| 558 // Only continuations are lifted, but in doing so, all let bindings inside | |
| 559 // the continuation are also lifted with it. we therefore restrict the | |
| 560 // lifting of the continuation that encloses [child]. | |
| 561 Continuation cont = enclosingContinuation[child]; | |
| 562 | |
| 563 // If [child] is in the top-level it cannot be lifted, hence no restrictions | |
| 564 // are necessary | |
| 565 if (cont == null) return; | |
| 566 | |
| 567 // If [child] and [ancestor] are in the same continuation, their nesting | |
| 568 // order is unaffected by lifting, so no restrictions are necessary. | |
| 569 if (enclosingContinuation[ancestor] == cont) return; | |
| 570 | |
| 571 // The continuation may not be lifted further than [ancestor]. | |
| 572 liftTarget[cont] = join(liftTarget[cont], ancestor); | |
| 573 | |
| 574 assert(liftTarget[cont] != cont); | |
| 575 } | |
| 576 | |
| 577 /// Returns the deepest of the two given scopes. The nesting order of scopes | |
|
Kevin Millikin (Google)
2014/06/10 11:33:01
For doc comments, the first paragraph should be a
| |
| 578 /// may be affected by lifting, but this function will restrict lifting of | |
| 579 /// continuations to ensure that the returned scope remains the most deeply | |
| 580 /// nested scope also after lifting. | |
| 581 Node join(Node s1, Node s2) { | |
|
Kevin Millikin (Google)
2014/06/10 11:33:01
Maybe this reads better with a descriptive name.
| |
| 582 if (s1 == null) return s2; | |
| 583 if (s2 == null) return s1; | |
| 584 if (s1 == s2) return s1; | |
| 585 int d1 = scope2depth[s1]; | |
| 586 int d2 = scope2depth[s2]; | |
| 587 assert(d1 != d2); | |
| 588 if (d1 < d2) { // s1 is ancestor of s2? | |
| 589 requireAncestor(s2, s1); // ensure that s2 remains inside of s1 | |
| 590 return s2; | |
| 591 } else { | |
| 592 requireAncestor(s1, s2); // ensure that s1 remains inside of s2 | |
| 593 return s1; | |
| 594 } | |
| 595 } | |
| 596 | |
| 597 /// Returns the continuations that could be lifted to the given scope. | |
| 598 Iterable<Continuation> getContinuationsLiftedTo(Node scope) { | |
| 599 List list = liftedContinuations[scope]; | |
| 600 if (list != null) | |
|
Kevin Millikin (Google)
2014/06/10 11:33:01
Put return list on the same line as the if. Other
| |
| 601 return list; | |
| 602 return const []; | |
| 603 } | |
| 604 | |
| 605 void visitFunctionDefinition(FunctionDefinition node) { | |
| 606 scope2depth[node] = 0; | |
| 607 ++currentDepth; | |
| 608 visit(node.body); | |
| 609 --currentDepth; | |
| 610 | |
| 611 // Build inverse of liftTarget | |
| 612 for (Continuation cont in liftTarget.keys) { | |
| 613 Node target = liftTarget[cont]; | |
| 614 List<Continuation> list = liftedContinuations[target]; | |
| 615 if (list == null) { | |
| 616 list = <Continuation>[]; | |
| 617 liftedContinuations[target] = list; | |
| 618 } | |
| 619 list.add(cont); | |
| 620 } | |
| 621 } | |
| 622 | |
| 623 // visit returns the outermost scope to which the given expression | |
| 624 // can be lifted without breaking scope. | |
| 625 | |
| 626 Node visitReference(Reference ref) { | |
| 627 Definition definition = ref.definition; | |
| 628 if (definition is Continuation) { | |
| 629 // Non-recursive reference to a continuation. Its lift target has been | |
| 630 // completely resolved by now since we are inside the LetCont binding. | |
| 631 // This reference can be lifted as far as the continuation. | |
| 632 return liftTarget[definition]; | |
| 633 } else { | |
| 634 return definition.binding; | |
| 635 } | |
| 636 } | |
| 637 | |
| 638 Node visitReferenceList(List<Reference> refs) { | |
| 639 Node scope = null; | |
| 640 for (Reference ref in refs) { | |
| 641 scope = join(scope, visitReference(ref)); | |
| 642 } | |
| 643 return scope; | |
| 644 } | |
| 645 | |
| 646 Node visitRecursiveReference(Reference ref) { | |
| 647 // A recursive self-reference inside a continuation can be lifted to the | |
| 648 // body of the continuation, not the body of the LetCont. | |
| 649 return ref.definition as Continuation; | |
| 650 } | |
| 651 | |
| 652 Node visitLetPrim(LetPrim node) { | |
| 653 Node primScope = visit(node.primitive); | |
| 654 scope2depth[node] = currentDepth; | |
| 655 enclosingContinuation[node] = currentContinuation; | |
| 656 ++currentDepth; | |
| 657 Node bodyScope = visit(node.body); | |
| 658 --currentDepth; | |
| 659 return join(primScope, bodyScope); | |
| 660 } | |
| 661 | |
| 662 Node visitLetCont(LetCont node) { | |
| 663 scope2depth[node] = currentDepth; | |
| 664 enclosingContinuation[node] = currentContinuation; | |
| 665 ++currentDepth; | |
| 666 visit(node.continuation); | |
| 667 Node bodyScope = visit(node.body); | |
| 668 --currentDepth; | |
| 669 return bodyScope; | |
| 670 } | |
| 671 | |
| 672 Node visitInvokeStatic(InvokeStatic node) { | |
| 673 Node argScope = visitReferenceList(node.arguments); | |
| 674 Node contScope = visitReference(node.continuation); | |
| 675 return join(argScope, contScope); | |
| 676 } | |
| 677 | |
| 678 Node visitInvokeContinuation(InvokeContinuation node) { | |
| 679 Node argScope = visitReferenceList(node.arguments); | |
| 680 Node contScope = node.isRecursive | |
| 681 ? visitRecursiveReference(node.continuation) | |
| 682 : visitReference(node.continuation); | |
| 683 return join(argScope, contScope); | |
| 684 } | |
| 685 | |
| 686 Node visitInvokeMethod(InvokeMethod node) { | |
| 687 Node receiverScope = visitReference(node.receiver); | |
| 688 Node argScope = visitReferenceList(node.arguments); | |
| 689 Node contScope = visitReference(node.continuation); | |
| 690 return join(receiverScope, join(argScope, contScope)); | |
| 691 } | |
| 692 | |
| 693 Node visitInvokeConstructor(InvokeConstructor node) { | |
| 694 Node argScope = visitReferenceList(node.arguments); | |
| 695 Node contScope = visitReference(node.continuation); | |
| 696 return join(argScope, contScope); | |
| 697 } | |
| 698 | |
| 699 Node visitConcatenateStrings(ConcatenateStrings node) { | |
| 700 Node argScope = visitReferenceList(node.arguments); | |
| 701 Node contScope = visitReference(node.continuation); | |
| 702 return join(argScope, contScope); | |
| 703 } | |
| 704 | |
| 705 Node visitBranch(Branch node) { | |
| 706 Node condScope = visit(node.condition); | |
| 707 Node thenScope = visitReference(node.trueContinuation); | |
| 708 Node elseScope = visitReference(node.falseContinuation); | |
| 709 return join(condScope, join(thenScope, elseScope)); | |
| 710 } | |
| 711 | |
| 712 Node visitInvokeConstConstructor(InvokeConstConstructor node) { | |
| 713 return visitReferenceList(node.arguments); | |
| 714 } | |
| 715 | |
| 716 Node visitLiteralList(LiteralList node) { | |
| 717 return visitReferenceList(node.values); | |
| 718 } | |
| 719 | |
| 720 Node visitLiteralMap(LiteralMap node) { | |
| 721 Node keyScope = visitReferenceList(node.keys); | |
| 722 Node valueScope = visitReferenceList(node.values); | |
| 723 return join(keyScope, valueScope); | |
| 724 } | |
| 725 | |
| 726 Node visitConstant(Constant node) { | |
| 727 return null; | |
| 728 } | |
| 729 | |
| 730 Node visitParameter(Parameter node) { | |
| 731 throw "Parameters should not be visited"; | |
| 732 } | |
| 733 | |
| 734 void visitContinuation(Continuation node) { | |
| 735 enclosingContinuation[node] = node; | |
| 736 liftTarget[node] = null; | |
| 737 scope2depth[node] = currentDepth; | |
| 738 Continuation oldCont = currentContinuation; | |
| 739 currentContinuation = node; | |
| 740 ++currentDepth; | |
| 741 Node bodyScope = visit(node.body); | |
| 742 --currentDepth; | |
| 743 // If the body contains a reference to one of the continuation parameters, | |
| 744 // then the scope will be the continuation itself, and the nesting | |
| 745 // restrictions on the continuation will have been added by join(). | |
| 746 // In other cases we must add the nesting restrictions here. | |
| 747 if (bodyScope != node) { | |
| 748 requireAncestor(node, bodyScope); | |
| 749 } | |
| 750 currentContinuation = oldCont; | |
| 751 } | |
| 752 | |
| 753 Node visitIsTrue(IsTrue node) { | |
| 754 return visitReference(node.value); | |
| 755 } | |
| 756 | |
| 757 } | |
| 758 | |
| 759 /// Keeps track of currently unused register indices. | |
| 760 class RegisterArray { | |
| 761 int nextIndex = 0; | |
| 762 final List<int> freeStack = <int>[]; | |
| 763 | |
| 764 int makeIndex() { | |
| 765 if (freeStack.isEmpty) { | |
| 766 return nextIndex++; | |
| 767 } else { | |
| 768 return freeStack.removeLast(); | |
| 769 } | |
| 770 } | |
| 771 | |
| 772 void releaseIndex(int index) { | |
| 773 freeStack.add(index); | |
| 774 } | |
| 775 } | |
| 776 | |
| 777 /// Assigns indices to each primitive in the IR such that primitives that are | |
| 778 /// live simultaneously never get assigned the same index. | |
| 779 /// This information is used by the dart tree builder to generate fewer | |
| 780 /// redundant variables. | |
| 781 /// Currently, the liveness analysis is very simple and is often inadequate | |
| 782 /// for removing all of the redundant variables. | |
| 783 class RegisterAllocator extends Visitor { | |
| 784 final FindLiftableContinuations lifts = new FindLiftableContinuations(); | |
| 785 | |
| 786 /// Separate register spaces for each source-level variable/parameter. | |
| 787 /// Note that null is used as key for primitives without elements. | |
| 788 final Map<Element, RegisterArray> elementRegisters = | |
| 789 <Element, RegisterArray>{}; | |
| 790 | |
| 791 RegisterArray getRegisterArray(Element element) { | |
| 792 RegisterArray registers = elementRegisters[element]; | |
| 793 if (registers == null) { | |
| 794 registers = new RegisterArray(); | |
| 795 elementRegisters[element] = registers; | |
| 796 } | |
| 797 return registers; | |
| 798 } | |
| 799 | |
| 800 void allocate(Primitive primitive) { | |
| 801 if (primitive.registerIndex == null) { | |
| 802 primitive.registerIndex = getRegisterArray(primitive.element).makeIndex(); | |
| 803 } | |
| 804 } | |
| 805 | |
| 806 void release(Primitive primitive) { | |
| 807 // Do not share indices for temporaries as this may obstruct inlining. | |
| 808 if (primitive.element == null) return; | |
| 809 if (primitive.registerIndex != null) { | |
| 810 getRegisterArray(primitive.element).releaseIndex(primitive.registerIndex); | |
| 811 } | |
| 812 } | |
| 813 | |
| 814 void visitLiftedContinuations(Node scope) { | |
| 815 for (Continuation cont in lifts.getContinuationsLiftedTo(scope)) { | |
| 816 assert(cont != scope); | |
| 817 visit(cont); | |
| 818 } | |
| 819 } | |
| 820 | |
| 821 void visitReference(Reference reference) { | |
| 822 allocate(reference.definition); | |
| 823 } | |
| 824 | |
| 825 void visitFunctionDefinition(FunctionDefinition node) { | |
| 826 lifts.visit(node); | |
| 827 visitLiftedContinuations(node); | |
| 828 visitLiftedContinuations(null); | |
| 829 visit(node.body); | |
| 830 node.parameters.forEach(allocate); // Assign indices to unused parameters. | |
| 831 elementRegisters.clear(); | |
| 832 } | |
| 833 | |
| 834 void visitLetPrim(LetPrim node) { | |
| 835 visitLiftedContinuations(node); | |
| 836 visit(node.body); | |
| 837 release(node.primitive); | |
| 838 visit(node.primitive); | |
| 839 } | |
| 840 | |
| 841 void visitLetCont(LetCont node) { | |
| 842 assert(lifts.liftTarget.containsKey(node.continuation)); | |
| 843 visit(node.body); | |
| 844 } | |
| 845 | |
| 846 void visitInvokeStatic(InvokeStatic node) { | |
| 847 node.arguments.forEach(visitReference); | |
| 848 } | |
| 849 | |
| 850 void visitInvokeContinuation(InvokeContinuation node) { | |
| 851 node.arguments.forEach(visitReference); | |
| 852 } | |
| 853 | |
| 854 void visitInvokeMethod(InvokeMethod node) { | |
| 855 visitReference(node.receiver); | |
| 856 node.arguments.forEach(visitReference); | |
| 857 } | |
| 858 | |
| 859 void visitInvokeConstructor(InvokeConstructor node) { | |
| 860 node.arguments.forEach(visitReference); | |
| 861 } | |
| 862 | |
| 863 void visitConcatenateStrings(ConcatenateStrings node) { | |
| 864 node.arguments.forEach(visitReference); | |
| 865 } | |
| 866 | |
| 867 void visitBranch(Branch node) { | |
| 868 visit(node.condition); | |
| 869 } | |
| 870 | |
| 871 void visitInvokeConstConstructor(InvokeConstConstructor node) { | |
| 872 node.arguments.forEach(visitReference); | |
| 873 } | |
| 874 | |
| 875 void visitLiteralList(LiteralList node) { | |
| 876 node.values.forEach(visitReference); | |
| 877 } | |
| 878 | |
| 879 void visitLiteralMap(LiteralMap node) { | |
| 880 for (int i = 0; i < node.keys.length; ++i) { | |
| 881 visitReference(node.keys[i]); | |
| 882 visitReference(node.values[i]); | |
| 883 } | |
| 884 } | |
| 885 | |
| 886 void visitConstant(Constant node) { | |
| 887 } | |
| 888 | |
| 889 void visitParameter(Parameter node) { | |
| 890 throw "Parameters should not be visited by RegisterAllocator"; | |
| 891 } | |
| 892 | |
| 893 void visitContinuation(Continuation node) { | |
| 894 visitLiftedContinuations(node); | |
| 895 visit(node.body); | |
| 896 | |
| 897 // Arguments get allocated left-to-right, so we release parameters | |
| 898 // right-to-left. This increases the likelihood that arguments can be | |
| 899 // transferred without intermediate assignments. | |
| 900 for (int i = node.parameters.length - 1; i >= 0; --i) { | |
| 901 release(node.parameters[i]); | |
| 902 } | |
| 903 } | |
| 904 | |
| 905 void visitIsTrue(IsTrue node) { | |
| 906 visitReference(node.value); | |
| 907 } | |
| 908 | |
| 909 } | |
| OLD | NEW |