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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart

Issue 312793002: dart2dart: Preserve variable names throughout the IR. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Handle InvokeConstConstructor Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698