OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 dart2js.cps_ir.optimize_interceptors; | 5 library dart2js.cps_ir.optimize_interceptors; |
6 | 6 |
7 import 'optimizers.dart'; | 7 import 'optimizers.dart'; |
8 import 'cps_ir_nodes.dart'; | 8 import 'cps_ir_nodes.dart'; |
9 import 'loop_hierarchy.dart'; | 9 import 'loop_hierarchy.dart'; |
10 import 'cps_fragment.dart'; | 10 import 'cps_fragment.dart'; |
11 import '../constants/values.dart'; | 11 import '../constants/values.dart'; |
12 import '../elements/elements.dart'; | 12 import '../elements/elements.dart'; |
13 import '../js_backend/backend_helpers.dart' show BackendHelpers; | 13 import '../js_backend/backend_helpers.dart' show BackendHelpers; |
14 import '../js_backend/js_backend.dart' show JavaScriptBackend; | 14 import '../js_backend/js_backend.dart' show JavaScriptBackend; |
15 import '../types/types.dart' show TypeMask; | 15 import '../types/types.dart' show TypeMask; |
16 import '../io/source_information.dart' show SourceInformation; | 16 import '../io/source_information.dart' show SourceInformation; |
| 17 import '../universe/selector.dart'; |
17 | 18 |
18 /// Replaces `getInterceptor` calls with interceptor constants when possible, | 19 /// Replaces `getInterceptor` calls with interceptor constants when possible, |
19 /// or with "almost constant" expressions like "x && CONST" when the input | 20 /// or with "almost constant" expressions like "x && CONST" when the input |
20 /// is either null or has a known interceptor. | 21 /// is either null or has a known interceptor. |
21 // | 22 // |
22 // TODO(asgerf): Compute intercepted classes in this pass. | 23 // TODO(asgerf): Compute intercepted classes in this pass. |
23 class OptimizeInterceptors extends TrampolineRecursiveVisitor implements Pass { | 24 class OptimizeInterceptors extends TrampolineRecursiveVisitor implements Pass { |
24 String get passName => 'Optimize interceptors'; | 25 String get passName => 'Optimize interceptors'; |
25 | 26 |
26 JavaScriptBackend backend; | 27 JavaScriptBackend backend; |
27 LoopHierarchy loopHierarchy; | 28 LoopHierarchy loopHierarchy; |
28 Continuation currentLoopHeader; | 29 Continuation currentLoopHeader; |
29 | 30 |
30 OptimizeInterceptors(this.backend); | 31 OptimizeInterceptors(this.backend); |
31 | 32 |
32 BackendHelpers get helpers => backend.helpers; | 33 BackendHelpers get helpers => backend.helpers; |
33 | 34 |
| 35 Map<Interceptor, Continuation> loopHeaderFor = <Interceptor, Continuation>{}; |
| 36 |
34 void rewrite(FunctionDefinition node) { | 37 void rewrite(FunctionDefinition node) { |
35 // TODO(asgerf): Computing the LoopHierarchy here may be overkill when all | 38 // TODO(asgerf): Computing the LoopHierarchy here may be overkill when all |
36 // we want is to hoist constants out of loops. | 39 // we want is to hoist constants out of loops. |
37 loopHierarchy = new LoopHierarchy(node); | 40 loopHierarchy = new LoopHierarchy(node); |
38 visit(node.body); | 41 visit(node.body); |
39 new ShareConstants().visit(node); | 42 new ShareConstants().visit(node); |
40 } | 43 } |
41 | 44 |
42 @override | 45 @override |
43 Expression traverseContinuation(Continuation cont) { | 46 Expression traverseContinuation(Continuation cont) { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
94 Continuation loop = getCurrentOuterLoop(); | 97 Continuation loop = getCurrentOuterLoop(); |
95 if (loop != null) { | 98 if (loop != null) { |
96 LetCont loopBinding = loop.parent; | 99 LetCont loopBinding = loop.parent; |
97 letPrim.insertAbove(loopBinding); | 100 letPrim.insertAbove(loopBinding); |
98 } else { | 101 } else { |
99 letPrim.insertAbove(useSite); | 102 letPrim.insertAbove(useSite); |
100 } | 103 } |
101 return prim; | 104 return prim; |
102 } | 105 } |
103 | 106 |
104 void constifyInterceptor(Interceptor interceptor) { | 107 bool constifyInterceptor(Interceptor interceptor) { |
105 LetPrim let = interceptor.parent; | 108 LetPrim let = interceptor.parent; |
106 InterceptorConstantValue constant = getInterceptorConstant(interceptor); | 109 InterceptorConstantValue constant = getInterceptorConstant(interceptor); |
107 | 110 |
108 if (constant == null) return; | 111 if (constant == null) return false; |
109 | 112 |
110 if (interceptor.isAlwaysIntercepted) { | 113 if (interceptor.isAlwaysIntercepted) { |
111 Primitive constantPrim = makeConstantFor(constant, | 114 Primitive constantPrim = makeConstantFor(constant, |
112 useSite: let, | 115 useSite: let, |
113 type: interceptor.type, | 116 type: interceptor.type, |
114 sourceInformation: interceptor.sourceInformation); | 117 sourceInformation: interceptor.sourceInformation); |
115 constantPrim.useElementAsHint(interceptor.hint); | 118 constantPrim.useElementAsHint(interceptor.hint); |
116 interceptor..replaceUsesWith(constantPrim)..destroy(); | 119 interceptor..replaceUsesWith(constantPrim)..destroy(); |
117 let.remove(); | 120 let.remove(); |
118 } else if (interceptor.isAlwaysNullOrIntercepted) { | 121 } else if (interceptor.isAlwaysNullOrIntercepted) { |
(...skipping 15 matching lines...) Expand all Loading... |
134 BuiltinOperator.LooseEq, | 137 BuiltinOperator.LooseEq, |
135 [input, cps.makeNull()]); | 138 [input, cps.makeNull()]); |
136 cps.ifTruthy(condition).invokeContinuation(cont, [input]); | 139 cps.ifTruthy(condition).invokeContinuation(cont, [input]); |
137 } | 140 } |
138 cps.invokeContinuation(cont, [constantPrim]); | 141 cps.invokeContinuation(cont, [constantPrim]); |
139 cps.context = cont; | 142 cps.context = cont; |
140 cps.insertAbove(let); | 143 cps.insertAbove(let); |
141 interceptor..replaceUsesWith(param)..destroy(); | 144 interceptor..replaceUsesWith(param)..destroy(); |
142 let.remove(); | 145 let.remove(); |
143 } | 146 } |
| 147 return true; |
144 } | 148 } |
145 | 149 |
146 @override | 150 @override |
147 Expression traverseLetPrim(LetPrim node) { | 151 Expression traverseLetPrim(LetPrim node) { |
148 Expression next = node.body; | 152 Expression next = node.body; |
149 if (node.primitive is Interceptor) { | 153 visit(node.primitive); |
150 constifyInterceptor(node.primitive); | 154 return next; |
| 155 } |
| 156 |
| 157 @override |
| 158 void visitInterceptor(Interceptor node) { |
| 159 if (constifyInterceptor(node)) return; |
| 160 if (node.hasExactlyOneUse) { |
| 161 // Set the loop header on single-use interceptors so [visitInvokeMethod] |
| 162 // can determine if it should become a one-shot interceptor. |
| 163 loopHeaderFor[node] = currentLoopHeader; |
151 } | 164 } |
152 return next; | 165 } |
| 166 |
| 167 @override |
| 168 void visitInvokeMethod(InvokeMethod node) { |
| 169 if (node.callingConvention != CallingConvention.Intercepted) return; |
| 170 Primitive interceptor = node.receiver.definition; |
| 171 if (interceptor is! Interceptor || |
| 172 interceptor.hasMultipleUses || |
| 173 loopHeaderFor[interceptor] != currentLoopHeader) { |
| 174 return; |
| 175 } |
| 176 // TODO(asgerf): Consider heuristics for when to use one-shot interceptors. |
| 177 // E.g. using only one-shot interceptors with a fast path. |
| 178 node.callingConvention = CallingConvention.OneShotIntercepted; |
| 179 node..receiver.unlink()..receiver = node.arguments.removeAt(0); |
| 180 } |
| 181 |
| 182 @override |
| 183 void visitTypeTestViaFlag(TypeTestViaFlag node) { |
| 184 Primitive interceptor = node.interceptor.definition; |
| 185 if (interceptor is! Interceptor || |
| 186 interceptor.hasMultipleUses || |
| 187 loopHeaderFor[interceptor] != currentLoopHeader || |
| 188 !backend.mayGenerateInstanceofCheck(node.dartType)) { |
| 189 return; |
| 190 } |
| 191 Interceptor inter = interceptor; |
| 192 Primitive value = inter.input.definition; |
| 193 node.replaceWith(new TypeTest(value, node.dartType, [])..type = node.type); |
153 } | 194 } |
154 } | 195 } |
155 | 196 |
156 /// Shares interceptor constants when one is in scope of another. | 197 /// Shares interceptor constants when one is in scope of another. |
157 /// | 198 /// |
158 /// Interceptor optimization runs after GVN, hence this clean-up step is needed. | 199 /// Interceptor optimization runs after GVN, hence this clean-up step is needed. |
159 /// | 200 /// |
160 /// TODO(asgerf): Handle in separate constant optimization pass? With some other | 201 /// TODO(asgerf): Handle in separate constant optimization pass? With some other |
161 /// constant-related optimizations, like cloning small constants at use-site. | 202 /// constant-related optimizations, like cloning small constants at use-site. |
162 class ShareConstants extends TrampolineRecursiveVisitor { | 203 class ShareConstants extends TrampolineRecursiveVisitor { |
(...skipping 16 matching lines...) Expand all Loading... |
179 sharedConstantFor.remove(prim.value); | 220 sharedConstantFor.remove(prim.value); |
180 }); | 221 }); |
181 } | 222 } |
182 return next; | 223 return next; |
183 } | 224 } |
184 | 225 |
185 bool shouldShareConstant(Constant constant) { | 226 bool shouldShareConstant(Constant constant) { |
186 return constant.value.isInterceptor; | 227 return constant.value.isInterceptor; |
187 } | 228 } |
188 } | 229 } |
OLD | NEW |