| 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 |