OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library tree_ir_builder; | 5 library tree_ir_builder; |
6 | 6 |
7 import '../common.dart'; | 7 import '../common.dart'; |
8 import '../constants/values.dart'; | 8 import '../constants/values.dart'; |
9 import '../cps_ir/cps_ir_nodes.dart' as cps_ir; | 9 import '../cps_ir/cps_ir_nodes.dart' as cps_ir; |
10 import '../elements/elements.dart'; | 10 import '../elements/elements.dart'; |
11 import 'package:js_ast/js_ast.dart' as js; | 11 import 'package:js_ast/js_ast.dart' as js; |
| 12 import '../js_backend/codegen/glue.dart'; |
12 | 13 |
13 import 'tree_ir_nodes.dart'; | 14 import 'tree_ir_nodes.dart'; |
14 | 15 |
15 typedef Statement NodeCallback(Statement next); | 16 typedef Statement NodeCallback(Statement next); |
16 | 17 |
17 /** | 18 /** |
18 * Builder translates from CPS-based IR to direct-style Tree. | 19 * Builder translates from CPS-based IR to direct-style Tree. |
19 * | 20 * |
20 * A call `Invoke(fun, cont, args)`, where cont is a singly-referenced | 21 * A call `Invoke(fun, cont, args)`, where cont is a singly-referenced |
21 * non-exit continuation `Cont(v, body)` is translated into a direct-style call | 22 * non-exit continuation `Cont(v, body)` is translated into a direct-style call |
(...skipping 19 matching lines...) Expand all Loading... |
41 * Block arguments are later replaced with data flow during the Tree-to-Tree | 42 * Block arguments are later replaced with data flow during the Tree-to-Tree |
42 * translation out of SSA. Jumps are eliminated during the Tree-to-Tree | 43 * translation out of SSA. Jumps are eliminated during the Tree-to-Tree |
43 * control-flow recognition. | 44 * control-flow recognition. |
44 * | 45 * |
45 * Otherwise, the output of Builder looks very much like the input. In | 46 * Otherwise, the output of Builder looks very much like the input. In |
46 * particular, intermediate values and blocks used for local control flow are | 47 * particular, intermediate values and blocks used for local control flow are |
47 * still all named. | 48 * still all named. |
48 */ | 49 */ |
49 class Builder implements cps_ir.Visitor/*<NodeCallback|Node>*/ { | 50 class Builder implements cps_ir.Visitor/*<NodeCallback|Node>*/ { |
50 final InternalErrorFunction internalError; | 51 final InternalErrorFunction internalError; |
| 52 final Glue glue; |
51 | 53 |
52 final Map<cps_ir.Primitive, Variable> primitive2variable = | 54 final Map<cps_ir.Primitive, Variable> primitive2variable = |
53 <cps_ir.Primitive, Variable>{}; | 55 <cps_ir.Primitive, Variable>{}; |
54 final Map<cps_ir.MutableVariable, Variable> mutable2variable = | 56 final Map<cps_ir.MutableVariable, Variable> mutable2variable = |
55 <cps_ir.MutableVariable, Variable>{}; | 57 <cps_ir.MutableVariable, Variable>{}; |
56 final Set<cps_ir.Constant> inlinedConstants = new Set<cps_ir.Constant>(); | 58 final Set<cps_ir.Constant> inlinedConstants = new Set<cps_ir.Constant>(); |
57 | 59 |
58 // Continuations with more than one use are replaced with Tree labels. This | 60 // Continuations with more than one use are replaced with Tree labels. This |
59 // is the mapping from continuations to labels. | 61 // is the mapping from continuations to labels. |
60 final Map<cps_ir.Continuation, Label> labels = <cps_ir.Continuation, Label>{}; | 62 final Map<cps_ir.Continuation, Label> labels = <cps_ir.Continuation, Label>{}; |
61 | 63 |
62 ExecutableElement currentElement; | 64 ExecutableElement currentElement; |
63 /// The 'this' Parameter for currentElement or the enclosing method. | 65 /// The parameter to be translated to 'this'. This can either be the receiver |
| 66 /// parameter, the interceptor parameter, or null if the method has neither. |
64 cps_ir.Parameter thisParameter; | 67 cps_ir.Parameter thisParameter; |
65 cps_ir.Continuation returnContinuation; | 68 cps_ir.Continuation returnContinuation; |
66 | 69 |
67 Builder parent; | 70 Builder(this.internalError, this.glue); |
68 | |
69 Builder(this.internalError, [this.parent]); | |
70 | |
71 Builder createInnerBuilder() { | |
72 return new Builder(internalError, this); | |
73 } | |
74 | 71 |
75 /// Variable used in [buildPhiAssignments] as a temporary when swapping | 72 /// Variable used in [buildPhiAssignments] as a temporary when swapping |
76 /// variables. | 73 /// variables. |
77 Variable phiTempVar; | 74 Variable phiTempVar; |
78 | 75 |
79 Variable addMutableVariable(cps_ir.MutableVariable irVariable) { | 76 Variable addMutableVariable(cps_ir.MutableVariable irVariable) { |
80 assert(!mutable2variable.containsKey(irVariable)); | 77 assert(!mutable2variable.containsKey(irVariable)); |
81 Variable variable = new Variable(currentElement, irVariable.hint); | 78 Variable variable = new Variable(currentElement, irVariable.hint); |
82 mutable2variable[irVariable] = variable; | 79 mutable2variable[irVariable] = variable; |
83 return variable; | 80 return variable; |
84 } | 81 } |
85 | 82 |
86 Variable getMutableVariable(cps_ir.MutableVariable mutableVariable) { | 83 Variable getMutableVariable(cps_ir.MutableVariable mutableVariable) { |
87 if (!mutable2variable.containsKey(mutableVariable)) { | |
88 return parent.getMutableVariable(mutableVariable)..isCaptured = true; | |
89 } | |
90 return mutable2variable[mutableVariable]; | 84 return mutable2variable[mutableVariable]; |
91 } | 85 } |
92 | 86 |
93 VariableUse getMutableVariableUse( | 87 VariableUse getMutableVariableUse( |
94 cps_ir.Reference<cps_ir.MutableVariable> reference) { | 88 cps_ir.Reference<cps_ir.MutableVariable> reference) { |
95 Variable variable = getMutableVariable(reference.definition); | 89 Variable variable = getMutableVariable(reference.definition); |
96 return new VariableUse(variable); | 90 return new VariableUse(variable); |
97 } | 91 } |
98 | 92 |
99 /// Obtains the variable representing the given primitive. Returns null for | 93 /// Obtains the variable representing the given primitive. Returns null for |
(...skipping 21 matching lines...) Expand all Loading... |
121 | 115 |
122 Expression getVariableUseOrNull( | 116 Expression getVariableUseOrNull( |
123 cps_ir.Reference<cps_ir.Primitive> reference) { | 117 cps_ir.Reference<cps_ir.Primitive> reference) { |
124 return reference == null ? null : getVariableUse(reference); | 118 return reference == null ? null : getVariableUse(reference); |
125 } | 119 } |
126 | 120 |
127 Label getLabel(cps_ir.Continuation cont) { | 121 Label getLabel(cps_ir.Continuation cont) { |
128 return labels.putIfAbsent(cont, () => new Label()); | 122 return labels.putIfAbsent(cont, () => new Label()); |
129 } | 123 } |
130 | 124 |
131 Variable addFunctionParameter(cps_ir.Parameter parameter) { | |
132 return getVariable(parameter); | |
133 } | |
134 | |
135 FunctionDefinition buildFunction(cps_ir.FunctionDefinition node) { | 125 FunctionDefinition buildFunction(cps_ir.FunctionDefinition node) { |
136 currentElement = node.element; | 126 currentElement = node.element; |
137 if (parent != null) { | 127 List<Variable> parameters = node.parameters.map(getVariable).toList(); |
138 // Local function's 'this' refers to enclosing method's 'this' | 128 if (node.interceptorParameter != null) { |
139 thisParameter = parent.thisParameter; | 129 parameters.insert(0, getVariable(node.receiverParameter)); |
| 130 thisParameter = glue.methodUsesReceiverArgument(node.element) |
| 131 ? node.interceptorParameter |
| 132 : node.receiverParameter; |
140 } else { | 133 } else { |
141 thisParameter = node.thisParameter; | 134 thisParameter = node.receiverParameter; |
142 } | 135 } |
143 List<Variable> parameters = | |
144 node.parameters.map(addFunctionParameter).toList(); | |
145 returnContinuation = node.returnContinuation; | 136 returnContinuation = node.returnContinuation; |
146 phiTempVar = new Variable(node.element, null); | 137 phiTempVar = new Variable(node.element, null); |
147 Statement body = translateExpression(node.body); | 138 Statement body = translateExpression(node.body); |
148 return new FunctionDefinition(node.element, parameters, body); | 139 return new FunctionDefinition(node.element, parameters, body); |
149 } | 140 } |
150 | 141 |
151 /// Returns a list of variables corresponding to the arguments to a method | 142 /// Returns a list of variables corresponding to the arguments to a method |
152 /// call or similar construct. | 143 /// call or similar construct. |
153 /// | 144 /// |
154 /// The `readCount` for these variables will be incremented. | 145 /// The `readCount` for these variables will be incremented. |
(...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
614 getVariableUse(node.indexRef), | 605 getVariableUse(node.indexRef), |
615 getVariableUse(node.valueRef)); | 606 getVariableUse(node.valueRef)); |
616 } | 607 } |
617 | 608 |
618 Expression visitInvokeStatic(cps_ir.InvokeStatic node) { | 609 Expression visitInvokeStatic(cps_ir.InvokeStatic node) { |
619 List<Expression> arguments = translateArguments(node.argumentRefs); | 610 List<Expression> arguments = translateArguments(node.argumentRefs); |
620 return new InvokeStatic(node.target, node.selector, arguments, | 611 return new InvokeStatic(node.target, node.selector, arguments, |
621 node.sourceInformation); | 612 node.sourceInformation); |
622 } | 613 } |
623 | 614 |
| 615 List<Expression> insertReceiverArgument(Expression receiver, |
| 616 List<Expression> arguments) { |
| 617 return new List<Expression>.generate(arguments.length + 1, |
| 618 (n) => n == 0 ? receiver : arguments[n - 1], |
| 619 growable: false); |
| 620 } |
| 621 |
624 Expression visitInvokeMethod(cps_ir.InvokeMethod node) { | 622 Expression visitInvokeMethod(cps_ir.InvokeMethod node) { |
625 if (node.callingConvention == cps_ir.CallingConvention.OneShotIntercepted) { | 623 switch (node.callingConvention) { |
626 List<Expression> arguments = new List.generate( | 624 case cps_ir.CallingConvention.Normal: |
627 1 + node.argumentRefs.length, | 625 InvokeMethod invoke = new InvokeMethod( |
628 (n) => getVariableUse(n == 0 ? node.receiverRef : node.argumentRefs[n
- 1]), | 626 getVariableUse(node.receiverRef), |
629 growable: false); | 627 node.selector, |
630 return new OneShotInterceptor(node.selector, node.mask, arguments, | 628 node.mask, |
631 node.sourceInformation); | 629 translateArguments(node.argumentRefs), |
| 630 node.sourceInformation); |
| 631 invoke.receiverIsNotNull = !node.receiver.type.isNullable; |
| 632 return invoke; |
| 633 |
| 634 case cps_ir.CallingConvention.Intercepted: |
| 635 List<Expression> arguments = insertReceiverArgument( |
| 636 getVariableUse(node.receiverRef), |
| 637 translateArguments(node.argumentRefs)); |
| 638 InvokeMethod invoke = new InvokeMethod( |
| 639 getVariableUse(node.interceptorRef), |
| 640 node.selector, |
| 641 node.mask, |
| 642 arguments, |
| 643 node.sourceInformation); |
| 644 // Sometimes we know the Dart receiver is non-null because it has been |
| 645 // refined, which implies that the JS receiver also can not be null at |
| 646 // the use-site. Interceptors are not refined, so this information is |
| 647 // not always available on the JS receiver. |
| 648 // Also check the JS receiver's type, however, because sometimes we know |
| 649 // an interceptor is non-null because it intercepts JSNull. |
| 650 invoke.receiverIsNotNull = |
| 651 !node.receiver.type.isNullable || |
| 652 !node.interceptor.type.isNullable; |
| 653 return invoke; |
| 654 |
| 655 case cps_ir.CallingConvention.DummyIntercepted: |
| 656 List<Expression> arguments = insertReceiverArgument( |
| 657 new Constant(new IntConstantValue(0)), |
| 658 translateArguments(node.argumentRefs)); |
| 659 InvokeMethod invoke = new InvokeMethod( |
| 660 getVariableUse(node.receiverRef), |
| 661 node.selector, |
| 662 node.mask, |
| 663 arguments, |
| 664 node.sourceInformation); |
| 665 invoke.receiverIsNotNull = !node.receiver.type.isNullable; |
| 666 return invoke; |
| 667 |
| 668 case cps_ir.CallingConvention.OneShotIntercepted: |
| 669 List<Expression> arguments = insertReceiverArgument( |
| 670 getVariableUse(node.receiverRef), |
| 671 translateArguments(node.argumentRefs)); |
| 672 return new OneShotInterceptor( |
| 673 node.selector, |
| 674 node.mask, |
| 675 arguments, |
| 676 node.sourceInformation); |
632 } | 677 } |
633 InvokeMethod invoke = new InvokeMethod( | |
634 getVariableUse(node.receiverRef), | |
635 node.selector, | |
636 node.mask, | |
637 translateArguments(node.argumentRefs), | |
638 node.sourceInformation); | |
639 // Sometimes we know the Dart receiver is non-null because it has been | |
640 // refined, which implies that the JS receiver also can not be null at the | |
641 // use-site. Interceptors are not refined, so this information is not | |
642 // always available on the JS receiver. | |
643 // Also check the JS receiver's type, however, because sometimes we know an | |
644 // interceptor is non-null because it intercepts JSNull. | |
645 invoke.receiverIsNotNull = | |
646 !node.dartReceiver.type.isNullable || | |
647 !node.receiver.type.isNullable; | |
648 return invoke; | |
649 } | 678 } |
650 | 679 |
651 Expression visitInvokeMethodDirectly(cps_ir.InvokeMethodDirectly node) { | 680 Expression visitInvokeMethodDirectly(cps_ir.InvokeMethodDirectly node) { |
652 Expression receiver = getVariableUse(node.receiverRef); | 681 if (node.interceptorRef != null) { |
653 List<Expression> arguments = translateArguments(node.argumentRefs); | 682 return new InvokeMethodDirectly(getVariableUse(node.interceptorRef), |
654 return new InvokeMethodDirectly(receiver, node.target, | 683 node.target, |
655 node.selector, arguments, node.sourceInformation); | 684 node.selector, |
| 685 insertReceiverArgument(getVariableUse(node.receiverRef), |
| 686 translateArguments(node.argumentRefs)), |
| 687 node.sourceInformation); |
| 688 } else { |
| 689 return new InvokeMethodDirectly(getVariableUse(node.receiverRef), |
| 690 node.target, |
| 691 node.selector, |
| 692 translateArguments(node.argumentRefs), |
| 693 node.sourceInformation); |
| 694 } |
656 } | 695 } |
657 | 696 |
658 Expression visitTypeCast(cps_ir.TypeCast node) { | 697 Expression visitTypeCast(cps_ir.TypeCast node) { |
659 Expression value = getVariableUse(node.valueRef); | 698 Expression value = getVariableUse(node.valueRef); |
660 List<Expression> typeArgs = translateArguments(node.typeArgumentRefs); | 699 List<Expression> typeArgs = translateArguments(node.typeArgumentRefs); |
661 return new TypeOperator(value, node.dartType, typeArgs, isTypeTest: false); | 700 return new TypeOperator(value, node.dartType, typeArgs, isTypeTest: false); |
662 } | 701 } |
663 | 702 |
664 Expression visitInvokeConstructor(cps_ir.InvokeConstructor node) { | 703 Expression visitInvokeConstructor(cps_ir.InvokeConstructor node) { |
665 List<Expression> arguments = translateArguments(node.argumentRefs); | 704 List<Expression> arguments = translateArguments(node.argumentRefs); |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
742 | 781 |
743 visitFunctionDefinition(cps_ir.FunctionDefinition node) { | 782 visitFunctionDefinition(cps_ir.FunctionDefinition node) { |
744 unexpectedNode(node); | 783 unexpectedNode(node); |
745 } | 784 } |
746 visitParameter(cps_ir.Parameter node) => unexpectedNode(node); | 785 visitParameter(cps_ir.Parameter node) => unexpectedNode(node); |
747 visitContinuation(cps_ir.Continuation node) => unexpectedNode(node); | 786 visitContinuation(cps_ir.Continuation node) => unexpectedNode(node); |
748 visitMutableVariable(cps_ir.MutableVariable node) => unexpectedNode(node); | 787 visitMutableVariable(cps_ir.MutableVariable node) => unexpectedNode(node); |
749 visitRethrow(cps_ir.Rethrow node) => unexpectedNode(node); | 788 visitRethrow(cps_ir.Rethrow node) => unexpectedNode(node); |
750 visitBoundsCheck(cps_ir.BoundsCheck node) => unexpectedNode(node); | 789 visitBoundsCheck(cps_ir.BoundsCheck node) => unexpectedNode(node); |
751 } | 790 } |
OLD | NEW |