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 part of ssa; | 5 part of ssa; |
6 | 6 |
7 /** | 7 /** |
8 * This phase simplifies interceptors in multiple ways: | 8 * This phase simplifies interceptors in multiple ways: |
9 * | 9 * |
10 * 1) If the interceptor is for an object whose type is known, it | 10 * 1) If the interceptor is for an object whose type is known, it |
(...skipping 16 matching lines...) Expand all Loading... |
27 class SsaSimplifyInterceptors extends HBaseVisitor | 27 class SsaSimplifyInterceptors extends HBaseVisitor |
28 implements OptimizationPhase { | 28 implements OptimizationPhase { |
29 final String name = "SsaSimplifyInterceptors"; | 29 final String name = "SsaSimplifyInterceptors"; |
30 final ConstantSystem constantSystem; | 30 final ConstantSystem constantSystem; |
31 final Compiler compiler; | 31 final Compiler compiler; |
32 final CodegenWorkItem work; | 32 final CodegenWorkItem work; |
33 HGraph graph; | 33 HGraph graph; |
34 | 34 |
35 SsaSimplifyInterceptors(this.compiler, this.constantSystem, this.work); | 35 SsaSimplifyInterceptors(this.compiler, this.constantSystem, this.work); |
36 | 36 |
| 37 JavaScriptBackend get backend => compiler.backend; |
| 38 |
| 39 BackendHelpers get helpers => backend.helpers; |
| 40 |
| 41 ClassWorld get classWorld => compiler.world; |
| 42 |
37 void visitGraph(HGraph graph) { | 43 void visitGraph(HGraph graph) { |
38 this.graph = graph; | 44 this.graph = graph; |
39 visitDominatorTree(graph); | 45 visitDominatorTree(graph); |
40 } | 46 } |
41 | 47 |
42 void visitBasicBlock(HBasicBlock node) { | 48 void visitBasicBlock(HBasicBlock node) { |
43 currentBlock = node; | 49 currentBlock = node; |
44 | 50 |
45 HInstruction instruction = node.first; | 51 HInstruction instruction = node.first; |
46 while (instruction != null) { | 52 while (instruction != null) { |
(...skipping 26 matching lines...) Expand all Loading... |
73 HInstruction constant = tryComputeConstantInterceptor( | 79 HInstruction constant = tryComputeConstantInterceptor( |
74 invoke.inputs[1], interceptor.interceptedClasses); | 80 invoke.inputs[1], interceptor.interceptedClasses); |
75 if (constant != null) { | 81 if (constant != null) { |
76 invoke.changeUse(interceptor, constant); | 82 invoke.changeUse(interceptor, constant); |
77 } | 83 } |
78 return false; | 84 return false; |
79 } | 85 } |
80 | 86 |
81 bool canUseSelfForInterceptor(HInstruction receiver, | 87 bool canUseSelfForInterceptor(HInstruction receiver, |
82 Set<ClassElement> interceptedClasses) { | 88 Set<ClassElement> interceptedClasses) { |
83 JavaScriptBackend backend = compiler.backend; | |
84 ClassWorld classWorld = compiler.world; | |
85 | 89 |
86 if (receiver.canBePrimitive(compiler)) { | 90 if (receiver.canBePrimitive(compiler)) { |
87 // Primitives always need interceptors. | 91 // Primitives always need interceptors. |
88 return false; | 92 return false; |
89 } | 93 } |
90 if (receiver.canBeNull() && | 94 if (receiver.canBeNull() && |
91 interceptedClasses.contains(backend.jsNullClass)) { | 95 interceptedClasses.contains(helpers.jsNullClass)) { |
92 // Need the JSNull interceptor. | 96 // Need the JSNull interceptor. |
93 return false; | 97 return false; |
94 } | 98 } |
95 | 99 |
96 // All intercepted classes extend `Interceptor`, so if the receiver can't be | 100 // All intercepted classes extend `Interceptor`, so if the receiver can't be |
97 // a class extending `Interceptor` then it can be called directly. | 101 // a class extending `Interceptor` then it can be called directly. |
98 return new TypeMask.nonNullSubclass(backend.jsInterceptorClass, classWorld) | 102 return new TypeMask.nonNullSubclass(helpers.jsInterceptorClass, classWorld) |
99 .intersection(receiver.instructionType, classWorld) | 103 .intersection(receiver.instructionType, classWorld) |
100 .isEmpty; | 104 .isEmpty; |
101 } | 105 } |
102 | 106 |
103 HInstruction tryComputeConstantInterceptor( | 107 HInstruction tryComputeConstantInterceptor( |
104 HInstruction input, | 108 HInstruction input, |
105 Set<ClassElement> interceptedClasses) { | 109 Set<ClassElement> interceptedClasses) { |
106 if (input == graph.explicitReceiverParameter) { | 110 if (input == graph.explicitReceiverParameter) { |
107 // If `explicitReceiverParameter` is set it means the current method is an | 111 // If `explicitReceiverParameter` is set it means the current method is an |
108 // interceptor method, and `this` is the interceptor. The caller just did | 112 // interceptor method, and `this` is the interceptor. The caller just did |
(...skipping 15 matching lines...) Expand all Loading... |
124 | 128 |
125 ConstantValue constant = | 129 ConstantValue constant = |
126 new InterceptorConstantValue(constantInterceptor.thisType); | 130 new InterceptorConstantValue(constantInterceptor.thisType); |
127 return graph.addConstant(constant, compiler); | 131 return graph.addConstant(constant, compiler); |
128 } | 132 } |
129 | 133 |
130 ClassElement tryComputeConstantInterceptorFromType( | 134 ClassElement tryComputeConstantInterceptorFromType( |
131 TypeMask type, | 135 TypeMask type, |
132 Set<ClassElement> interceptedClasses) { | 136 Set<ClassElement> interceptedClasses) { |
133 | 137 |
134 ClassWorld classWorld = compiler.world; | |
135 JavaScriptBackend backend = compiler.backend; | |
136 if (type.isNullable) { | 138 if (type.isNullable) { |
137 if (type.isEmpty) { | 139 if (type.isEmpty) { |
138 return backend.jsNullClass; | 140 return helpers.jsNullClass; |
139 } | 141 } |
140 } else if (type.containsOnlyInt(classWorld)) { | 142 } else if (type.containsOnlyInt(classWorld)) { |
141 return backend.jsIntClass; | 143 return helpers.jsIntClass; |
142 } else if (type.containsOnlyDouble(classWorld)) { | 144 } else if (type.containsOnlyDouble(classWorld)) { |
143 return backend.jsDoubleClass; | 145 return helpers.jsDoubleClass; |
144 } else if (type.containsOnlyBool(classWorld)) { | 146 } else if (type.containsOnlyBool(classWorld)) { |
145 return backend.jsBoolClass; | 147 return helpers.jsBoolClass; |
146 } else if (type.containsOnlyString(classWorld)) { | 148 } else if (type.containsOnlyString(classWorld)) { |
147 return backend.jsStringClass; | 149 return helpers.jsStringClass; |
148 } else if (type.satisfies(backend.jsArrayClass, classWorld)) { | 150 } else if (type.satisfies(helpers.jsArrayClass, classWorld)) { |
149 return backend.jsArrayClass; | 151 return helpers.jsArrayClass; |
150 } else if (type.containsOnlyNum(classWorld) && | 152 } else if (type.containsOnlyNum(classWorld) && |
151 !interceptedClasses.contains(backend.jsIntClass) && | 153 !interceptedClasses.contains(helpers.jsIntClass) && |
152 !interceptedClasses.contains(backend.jsDoubleClass)) { | 154 !interceptedClasses.contains(helpers.jsDoubleClass)) { |
153 // If the method being intercepted is not defined in [int] or [double] we | 155 // If the method being intercepted is not defined in [int] or [double] we |
154 // can safely use the number interceptor. This is because none of the | 156 // can safely use the number interceptor. This is because none of the |
155 // [int] or [double] methods are called from a method defined on [num]. | 157 // [int] or [double] methods are called from a method defined on [num]. |
156 return backend.jsNumberClass; | 158 return helpers.jsNumberClass; |
157 } else { | 159 } else { |
158 // Try to find constant interceptor for a native class. If the receiver | 160 // Try to find constant interceptor for a native class. If the receiver |
159 // is constrained to a leaf native class, we can use the class's | 161 // is constrained to a leaf native class, we can use the class's |
160 // interceptor directly. | 162 // interceptor directly. |
161 | 163 |
162 // TODO(sra): Key DOM classes like Node, Element and Event are not leaf | 164 // TODO(sra): Key DOM classes like Node, Element and Event are not leaf |
163 // classes. When the receiver type is not a leaf class, we might still be | 165 // classes. When the receiver type is not a leaf class, we might still be |
164 // able to use the receiver class as a constant interceptor. It is | 166 // able to use the receiver class as a constant interceptor. It is |
165 // usually the case that methods defined on a non-leaf class don't test | 167 // usually the case that methods defined on a non-leaf class don't test |
166 // for a subclass or call methods defined on a subclass. Provided the | 168 // for a subclass or call methods defined on a subclass. Provided the |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
202 // | 204 // |
203 // Currently we use the most general interceptor since all intercepted types | 205 // Currently we use the most general interceptor since all intercepted types |
204 // implement `hashCode`. But in this example, `a.hashCode` is only reached | 206 // implement `hashCode`. But in this example, `a.hashCode` is only reached |
205 // if `a.length` succeeds, which is indicated by the hashCode receiver being | 207 // if `a.length` succeeds, which is indicated by the hashCode receiver being |
206 // a HTypeKnown instruction. | 208 // a HTypeKnown instruction. |
207 | 209 |
208 int useCount(HInstruction user, HInstruction used) => | 210 int useCount(HInstruction user, HInstruction used) => |
209 user.inputs.where((input) => input == used).length; | 211 user.inputs.where((input) => input == used).length; |
210 | 212 |
211 Set<ClassElement> interceptedClasses; | 213 Set<ClassElement> interceptedClasses; |
212 JavaScriptBackend backend = compiler.backend; | |
213 HInstruction dominator = findDominator(node.usedBy); | 214 HInstruction dominator = findDominator(node.usedBy); |
214 // If there is a call that dominates all other uses, we can use just the | 215 // If there is a call that dominates all other uses, we can use just the |
215 // selector of that instruction. | 216 // selector of that instruction. |
216 if (dominator is HInvokeDynamic && | 217 if (dominator is HInvokeDynamic && |
217 dominator.isCallOnInterceptor(compiler) && | 218 dominator.isCallOnInterceptor(compiler) && |
218 node == dominator.receiver && | 219 node == dominator.receiver && |
219 useCount(dominator, node) == 1) { | 220 useCount(dominator, node) == 1) { |
220 interceptedClasses = | 221 interceptedClasses = |
221 backend.getInterceptedClassesOn(dominator.selector.name); | 222 backend.getInterceptedClassesOn(dominator.selector.name); |
222 | 223 |
223 // If we found that we need number, we must still go through all | 224 // If we found that we need number, we must still go through all |
224 // uses to check if they require int, or double. | 225 // uses to check if they require int, or double. |
225 if (interceptedClasses.contains(backend.jsNumberClass) && | 226 if (interceptedClasses.contains(helpers.jsNumberClass) && |
226 !(interceptedClasses.contains(backend.jsDoubleClass) || | 227 !(interceptedClasses.contains(helpers.jsDoubleClass) || |
227 interceptedClasses.contains(backend.jsIntClass))) { | 228 interceptedClasses.contains(helpers.jsIntClass))) { |
228 for (HInstruction user in node.usedBy) { | 229 for (HInstruction user in node.usedBy) { |
229 if (user is! HInvoke) continue; | 230 if (user is! HInvoke) continue; |
230 Set<ClassElement> intercepted = | 231 Set<ClassElement> intercepted = |
231 backend.getInterceptedClassesOn(user.selector.name); | 232 backend.getInterceptedClassesOn(user.selector.name); |
232 if (intercepted.contains(backend.jsIntClass)) { | 233 if (intercepted.contains(helpers.jsIntClass)) { |
233 interceptedClasses.add(backend.jsIntClass); | 234 interceptedClasses.add(helpers.jsIntClass); |
234 } | 235 } |
235 if (intercepted.contains(backend.jsDoubleClass)) { | 236 if (intercepted.contains(helpers.jsDoubleClass)) { |
236 interceptedClasses.add(backend.jsDoubleClass); | 237 interceptedClasses.add(helpers.jsDoubleClass); |
237 } | 238 } |
238 } | 239 } |
239 } | 240 } |
240 } else { | 241 } else { |
241 interceptedClasses = new Set<ClassElement>(); | 242 interceptedClasses = new Set<ClassElement>(); |
242 for (HInstruction user in node.usedBy) { | 243 for (HInstruction user in node.usedBy) { |
243 if (user is HInvokeDynamic && | 244 if (user is HInvokeDynamic && |
244 user.isCallOnInterceptor(compiler) && | 245 user.isCallOnInterceptor(compiler) && |
245 node == user.receiver && | 246 node == user.receiver && |
246 useCount(user, node) == 1) { | 247 useCount(user, node) == 1) { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
284 node.block.rewrite(node, constantInterceptor); | 285 node.block.rewrite(node, constantInterceptor); |
285 return false; | 286 return false; |
286 } | 287 } |
287 | 288 |
288 // Do we have an 'almost constant' interceptor? The receiver could be | 289 // Do we have an 'almost constant' interceptor? The receiver could be |
289 // `null` but not any other JavaScript falsy value, `null` values cause | 290 // `null` but not any other JavaScript falsy value, `null` values cause |
290 // `NoSuchMethodError`s, and if the receiver was not null we would have a | 291 // `NoSuchMethodError`s, and if the receiver was not null we would have a |
291 // constant interceptor `C`. Then we can use `(receiver && C)` for the | 292 // constant interceptor `C`. Then we can use `(receiver && C)` for the |
292 // interceptor. | 293 // interceptor. |
293 if (receiver.canBeNull() && !node.isConditionalConstantInterceptor) { | 294 if (receiver.canBeNull() && !node.isConditionalConstantInterceptor) { |
294 if (!interceptedClasses.contains(backend.jsNullClass)) { | 295 if (!interceptedClasses.contains(helpers.jsNullClass)) { |
295 // Can use `(receiver && C)` only if receiver is either null or truthy. | 296 // Can use `(receiver && C)` only if receiver is either null or truthy. |
296 if (!(receiver.canBePrimitiveNumber(compiler) || | 297 if (!(receiver.canBePrimitiveNumber(compiler) || |
297 receiver.canBePrimitiveBoolean(compiler) || | 298 receiver.canBePrimitiveBoolean(compiler) || |
298 receiver.canBePrimitiveString(compiler))) { | 299 receiver.canBePrimitiveString(compiler))) { |
299 ClassElement interceptorClass = tryComputeConstantInterceptorFromType( | 300 ClassElement interceptorClass = tryComputeConstantInterceptorFromType( |
300 receiver.instructionType.nonNullable(), interceptedClasses); | 301 receiver.instructionType.nonNullable(), interceptedClasses); |
301 if (interceptorClass != null) { | 302 if (interceptorClass != null) { |
302 HInstruction constantInstruction = | 303 HInstruction constantInstruction = |
303 graph.addConstant( | 304 graph.addConstant( |
304 new InterceptorConstantValue(interceptorClass.thisType), | 305 new InterceptorConstantValue(interceptorClass.thisType), |
(...skipping 19 matching lines...) Expand all Loading... |
324 block.addAfter(user, replacement); | 325 block.addAfter(user, replacement); |
325 block.rewrite(user, replacement); | 326 block.rewrite(user, replacement); |
326 block.remove(user); | 327 block.remove(user); |
327 return false; | 328 return false; |
328 } | 329 } |
329 | 330 |
330 if (user is HIs) { | 331 if (user is HIs) { |
331 // See if we can rewrite the is-check to use 'instanceof', i.e. rewrite | 332 // See if we can rewrite the is-check to use 'instanceof', i.e. rewrite |
332 // "getInterceptor(x).$isT" to "x instanceof T". | 333 // "getInterceptor(x).$isT" to "x instanceof T". |
333 if (node == user.interceptor) { | 334 if (node == user.interceptor) { |
334 JavaScriptBackend backend = compiler.backend; | |
335 if (backend.mayGenerateInstanceofCheck(user.typeExpression)) { | 335 if (backend.mayGenerateInstanceofCheck(user.typeExpression)) { |
336 HInstruction instanceofCheck = new HIs.instanceOf( | 336 HInstruction instanceofCheck = new HIs.instanceOf( |
337 user.typeExpression, user.expression, user.instructionType); | 337 user.typeExpression, user.expression, user.instructionType); |
338 instanceofCheck.sourceInformation = user.sourceInformation; | 338 instanceofCheck.sourceInformation = user.sourceInformation; |
339 instanceofCheck.sourceElement = user.sourceElement; | 339 instanceofCheck.sourceElement = user.sourceElement; |
340 return replaceUserWith(instanceofCheck); | 340 return replaceUserWith(instanceofCheck); |
341 } | 341 } |
342 } | 342 } |
343 } else if (user is HInvokeDynamic) { | 343 } else if (user is HInvokeDynamic) { |
344 if (node == user.inputs[0]) { | 344 if (node == user.inputs[0]) { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
403 instruction = new HInvokeDynamicMethod( | 403 instruction = new HInvokeDynamicMethod( |
404 selector, mask, inputs, node.instructionType, true); | 404 selector, mask, inputs, node.instructionType, true); |
405 } | 405 } |
406 | 406 |
407 HBasicBlock block = node.block; | 407 HBasicBlock block = node.block; |
408 block.addAfter(node, instruction); | 408 block.addAfter(node, instruction); |
409 block.rewrite(node, instruction); | 409 block.rewrite(node, instruction); |
410 return true; | 410 return true; |
411 } | 411 } |
412 } | 412 } |
OLD | NEW |