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 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; | 5 import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem; |
6 import '../compiler.dart' show Compiler; | 6 import '../compiler.dart' show Compiler; |
7 import '../constants/constant_system.dart'; | 7 import '../constants/constant_system.dart'; |
8 import '../constants/values.dart'; | 8 import '../constants/values.dart'; |
9 import '../elements/elements.dart'; | 9 import '../elements/elements.dart'; |
10 import '../js_backend/backend_helpers.dart' show BackendHelpers; | 10 import '../js_backend/backend_helpers.dart' show BackendHelpers; |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
89 // TODO(sra): Also do self-interceptor rewrites on a per-use basis. | 89 // TODO(sra): Also do self-interceptor rewrites on a per-use basis. |
90 | 90 |
91 HInstruction constant = tryComputeConstantInterceptor( | 91 HInstruction constant = tryComputeConstantInterceptor( |
92 invoke.inputs[1], interceptor.interceptedClasses); | 92 invoke.inputs[1], interceptor.interceptedClasses); |
93 if (constant != null) { | 93 if (constant != null) { |
94 invoke.changeUse(interceptor, constant); | 94 invoke.changeUse(interceptor, constant); |
95 } | 95 } |
96 return false; | 96 return false; |
97 } | 97 } |
98 | 98 |
99 bool canUseSelfForInterceptor(HInstruction receiver, | 99 bool canUseSelfForInterceptor( |
100 Set<ClassElement> interceptedClasses) { | 100 HInstruction receiver, Set<ClassElement> interceptedClasses) { |
101 | |
102 if (receiver.canBePrimitive(compiler)) { | 101 if (receiver.canBePrimitive(compiler)) { |
103 // Primitives always need interceptors. | 102 // Primitives always need interceptors. |
104 return false; | 103 return false; |
105 } | 104 } |
106 if (receiver.canBeNull() && | 105 if (receiver.canBeNull() && |
107 interceptedClasses.contains(helpers.jsNullClass)) { | 106 interceptedClasses.contains(helpers.jsNullClass)) { |
108 // Need the JSNull interceptor. | 107 // Need the JSNull interceptor. |
109 return false; | 108 return false; |
110 } | 109 } |
111 | 110 |
112 // All intercepted classes extend `Interceptor`, so if the receiver can't be | 111 // All intercepted classes extend `Interceptor`, so if the receiver can't be |
113 // a class extending `Interceptor` then it can be called directly. | 112 // a class extending `Interceptor` then it can be called directly. |
114 return new TypeMask.nonNullSubclass(helpers.jsInterceptorClass, classWorld) | 113 return new TypeMask.nonNullSubclass(helpers.jsInterceptorClass, classWorld) |
115 .isDisjoint(receiver.instructionType, classWorld); | 114 .isDisjoint(receiver.instructionType, classWorld); |
116 } | 115 } |
117 | 116 |
118 HInstruction tryComputeConstantInterceptor( | 117 HInstruction tryComputeConstantInterceptor( |
119 HInstruction input, | 118 HInstruction input, Set<ClassElement> interceptedClasses) { |
120 Set<ClassElement> interceptedClasses) { | |
121 if (input == graph.explicitReceiverParameter) { | 119 if (input == graph.explicitReceiverParameter) { |
122 // If `explicitReceiverParameter` is set it means the current method is an | 120 // If `explicitReceiverParameter` is set it means the current method is an |
123 // interceptor method, and `this` is the interceptor. The caller just did | 121 // interceptor method, and `this` is the interceptor. The caller just did |
124 // `getInterceptor(foo).currentMethod(foo)` to enter the current method. | 122 // `getInterceptor(foo).currentMethod(foo)` to enter the current method. |
125 return graph.thisInstruction; | 123 return graph.thisInstruction; |
126 } | 124 } |
127 | 125 |
128 ClassElement constantInterceptor = tryComputeConstantInterceptorFromType( | 126 ClassElement constantInterceptor = tryComputeConstantInterceptorFromType( |
129 input.instructionType, interceptedClasses); | 127 input.instructionType, interceptedClasses); |
130 | 128 |
131 if (constantInterceptor == null) return null; | 129 if (constantInterceptor == null) return null; |
132 | 130 |
133 // If we just happen to be in an instance method of the constant | 131 // If we just happen to be in an instance method of the constant |
134 // interceptor, `this` is a shorter alias. | 132 // interceptor, `this` is a shorter alias. |
135 if (constantInterceptor == work.element.enclosingClass && | 133 if (constantInterceptor == work.element.enclosingClass && |
136 graph.thisInstruction != null) { | 134 graph.thisInstruction != null) { |
137 return graph.thisInstruction; | 135 return graph.thisInstruction; |
138 } | 136 } |
139 | 137 |
140 ConstantValue constant = | 138 ConstantValue constant = |
141 new InterceptorConstantValue(constantInterceptor.thisType); | 139 new InterceptorConstantValue(constantInterceptor.thisType); |
142 return graph.addConstant(constant, compiler); | 140 return graph.addConstant(constant, compiler); |
143 } | 141 } |
144 | 142 |
145 ClassElement tryComputeConstantInterceptorFromType( | 143 ClassElement tryComputeConstantInterceptorFromType( |
146 TypeMask type, | 144 TypeMask type, Set<ClassElement> interceptedClasses) { |
147 Set<ClassElement> interceptedClasses) { | |
148 | |
149 if (type.isNullable) { | 145 if (type.isNullable) { |
150 if (type.isNull) { | 146 if (type.isNull) { |
151 return helpers.jsNullClass; | 147 return helpers.jsNullClass; |
152 } | 148 } |
153 } else if (type.containsOnlyInt(classWorld)) { | 149 } else if (type.containsOnlyInt(classWorld)) { |
154 return helpers.jsIntClass; | 150 return helpers.jsIntClass; |
155 } else if (type.containsOnlyDouble(classWorld)) { | 151 } else if (type.containsOnlyDouble(classWorld)) { |
156 return helpers.jsDoubleClass; | 152 return helpers.jsDoubleClass; |
157 } else if (type.containsOnlyBool(classWorld)) { | 153 } else if (type.containsOnlyBool(classWorld)) { |
158 return helpers.jsBoolClass; | 154 return helpers.jsBoolClass; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
223 | 219 |
224 Set<ClassElement> interceptedClasses; | 220 Set<ClassElement> interceptedClasses; |
225 HInstruction dominator = findDominator(node.usedBy); | 221 HInstruction dominator = findDominator(node.usedBy); |
226 // If there is a call that dominates all other uses, we can use just the | 222 // If there is a call that dominates all other uses, we can use just the |
227 // selector of that instruction. | 223 // selector of that instruction. |
228 if (dominator is HInvokeDynamic && | 224 if (dominator is HInvokeDynamic && |
229 dominator.isCallOnInterceptor(compiler) && | 225 dominator.isCallOnInterceptor(compiler) && |
230 node == dominator.receiver && | 226 node == dominator.receiver && |
231 useCount(dominator, node) == 1) { | 227 useCount(dominator, node) == 1) { |
232 interceptedClasses = | 228 interceptedClasses = |
233 backend.getInterceptedClassesOn(dominator.selector.name); | 229 backend.getInterceptedClassesOn(dominator.selector.name); |
234 | 230 |
235 // If we found that we need number, we must still go through all | 231 // If we found that we need number, we must still go through all |
236 // uses to check if they require int, or double. | 232 // uses to check if they require int, or double. |
237 if (interceptedClasses.contains(helpers.jsNumberClass) && | 233 if (interceptedClasses.contains(helpers.jsNumberClass) && |
238 !(interceptedClasses.contains(helpers.jsDoubleClass) || | 234 !(interceptedClasses.contains(helpers.jsDoubleClass) || |
239 interceptedClasses.contains(helpers.jsIntClass))) { | 235 interceptedClasses.contains(helpers.jsIntClass))) { |
240 for (HInstruction user in node.usedBy) { | 236 for (HInstruction user in node.usedBy) { |
241 if (user is! HInvoke) continue; | 237 if (user is! HInvoke) continue; |
242 Set<ClassElement> intercepted = | 238 Set<ClassElement> intercepted = |
243 backend.getInterceptedClassesOn(user.selector.name); | 239 backend.getInterceptedClassesOn(user.selector.name); |
244 if (intercepted.contains(helpers.jsIntClass)) { | 240 if (intercepted.contains(helpers.jsIntClass)) { |
245 interceptedClasses.add(helpers.jsIntClass); | 241 interceptedClasses.add(helpers.jsIntClass); |
246 } | 242 } |
247 if (intercepted.contains(helpers.jsDoubleClass)) { | 243 if (intercepted.contains(helpers.jsDoubleClass)) { |
248 interceptedClasses.add(helpers.jsDoubleClass); | 244 interceptedClasses.add(helpers.jsDoubleClass); |
249 } | 245 } |
250 } | 246 } |
251 } | 247 } |
252 } else { | 248 } else { |
253 interceptedClasses = new Set<ClassElement>(); | 249 interceptedClasses = new Set<ClassElement>(); |
254 for (HInstruction user in node.usedBy) { | 250 for (HInstruction user in node.usedBy) { |
255 if (user is HInvokeDynamic && | 251 if (user is HInvokeDynamic && |
256 user.isCallOnInterceptor(compiler) && | 252 user.isCallOnInterceptor(compiler) && |
257 node == user.receiver && | 253 node == user.receiver && |
258 useCount(user, node) == 1) { | 254 useCount(user, node) == 1) { |
259 interceptedClasses.addAll( | 255 interceptedClasses |
260 backend.getInterceptedClassesOn(user.selector.name)); | 256 .addAll(backend.getInterceptedClassesOn(user.selector.name)); |
261 } else if (user is HInvokeSuper && | 257 } else if (user is HInvokeSuper && |
262 user.isCallOnInterceptor(compiler) && | 258 user.isCallOnInterceptor(compiler) && |
263 node == user.receiver && | 259 node == user.receiver && |
264 useCount(user, node) == 1) { | 260 useCount(user, node) == 1) { |
265 interceptedClasses.addAll( | 261 interceptedClasses |
266 backend.getInterceptedClassesOn(user.selector.name)); | 262 .addAll(backend.getInterceptedClassesOn(user.selector.name)); |
267 } else { | 263 } else { |
268 // Use a most general interceptor for other instructions, example, | 264 // Use a most general interceptor for other instructions, example, |
269 // is-checks and escaping interceptors. | 265 // is-checks and escaping interceptors. |
270 interceptedClasses.addAll(backend.interceptedClasses); | 266 interceptedClasses.addAll(backend.interceptedClasses); |
271 break; | 267 break; |
272 } | 268 } |
273 } | 269 } |
274 } | 270 } |
275 | 271 |
276 node.interceptedClasses = interceptedClasses; | 272 node.interceptedClasses = interceptedClasses; |
(...skipping 27 matching lines...) Expand all Loading... |
304 // interceptor. | 300 // interceptor. |
305 if (receiver.canBeNull() && !node.isConditionalConstantInterceptor) { | 301 if (receiver.canBeNull() && !node.isConditionalConstantInterceptor) { |
306 if (!interceptedClasses.contains(helpers.jsNullClass)) { | 302 if (!interceptedClasses.contains(helpers.jsNullClass)) { |
307 // Can use `(receiver && C)` only if receiver is either null or truthy. | 303 // Can use `(receiver && C)` only if receiver is either null or truthy. |
308 if (!(receiver.canBePrimitiveNumber(compiler) || | 304 if (!(receiver.canBePrimitiveNumber(compiler) || |
309 receiver.canBePrimitiveBoolean(compiler) || | 305 receiver.canBePrimitiveBoolean(compiler) || |
310 receiver.canBePrimitiveString(compiler))) { | 306 receiver.canBePrimitiveString(compiler))) { |
311 ClassElement interceptorClass = tryComputeConstantInterceptorFromType( | 307 ClassElement interceptorClass = tryComputeConstantInterceptorFromType( |
312 receiver.instructionType.nonNullable(), interceptedClasses); | 308 receiver.instructionType.nonNullable(), interceptedClasses); |
313 if (interceptorClass != null) { | 309 if (interceptorClass != null) { |
314 HInstruction constantInstruction = | 310 HInstruction constantInstruction = graph.addConstant( |
315 graph.addConstant( | 311 new InterceptorConstantValue(interceptorClass.thisType), |
316 new InterceptorConstantValue(interceptorClass.thisType), | 312 compiler); |
317 compiler); | |
318 node.conditionalConstantInterceptor = constantInstruction; | 313 node.conditionalConstantInterceptor = constantInstruction; |
319 constantInstruction.usedBy.add(node); | 314 constantInstruction.usedBy.add(node); |
320 return false; | 315 return false; |
321 } | 316 } |
322 } | 317 } |
323 } | 318 } |
324 } | 319 } |
325 | 320 |
326 // Try creating a one-shot interceptor or optimized is-check | 321 // Try creating a one-shot interceptor or optimized is-check |
327 if (compiler.options.hasIncrementalSupport) return false; | 322 if (compiler.options.hasIncrementalSupport) return false; |
(...skipping 23 matching lines...) Expand all Loading... |
351 return replaceUserWith(instanceofCheck); | 346 return replaceUserWith(instanceofCheck); |
352 } | 347 } |
353 } | 348 } |
354 } else if (user is HInvokeDynamic) { | 349 } else if (user is HInvokeDynamic) { |
355 if (node == user.inputs[0]) { | 350 if (node == user.inputs[0]) { |
356 // Replace the user with a [HOneShotInterceptor]. | 351 // Replace the user with a [HOneShotInterceptor]. |
357 HConstant nullConstant = graph.addConstantNull(compiler); | 352 HConstant nullConstant = graph.addConstantNull(compiler); |
358 List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); | 353 List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); |
359 inputs[0] = nullConstant; | 354 inputs[0] = nullConstant; |
360 HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor( | 355 HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor( |
361 user.selector, user.mask, | 356 user.selector, |
362 inputs, user.instructionType, interceptedClasses); | 357 user.mask, |
| 358 inputs, |
| 359 user.instructionType, |
| 360 interceptedClasses); |
363 oneShotInterceptor.sourceInformation = user.sourceInformation; | 361 oneShotInterceptor.sourceInformation = user.sourceInformation; |
364 oneShotInterceptor.sourceElement = user.sourceElement; | 362 oneShotInterceptor.sourceElement = user.sourceElement; |
365 return replaceUserWith(oneShotInterceptor); | 363 return replaceUserWith(oneShotInterceptor); |
366 } | 364 } |
367 } | 365 } |
368 | 366 |
369 return false; | 367 return false; |
370 } | 368 } |
371 | 369 |
372 bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { | 370 bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { |
373 for (HInstruction user in node.usedBy.toList()) { | 371 for (HInstruction user in node.usedBy.toList()) { |
374 if (user is HIs) { | 372 if (user is HIs) { |
375 user.changeUse(node, receiver); | 373 user.changeUse(node, receiver); |
376 } else { | 374 } else { |
377 // Use the potentially self-argument as new receiver. Note that the | 375 // Use the potentially self-argument as new receiver. Note that the |
378 // self-argument could potentially have a tighter type than the | 376 // self-argument could potentially have a tighter type than the |
379 // receiver which was the input to the interceptor. | 377 // receiver which was the input to the interceptor. |
380 assert(user.inputs[0] == node); | 378 assert(user.inputs[0] == node); |
381 assert(receiver.nonCheck() == user.inputs[1].nonCheck()); | 379 assert(receiver.nonCheck() == user.inputs[1].nonCheck()); |
382 user.changeUse(node, user.inputs[1]); | 380 user.changeUse(node, user.inputs[1]); |
383 } | 381 } |
384 } | 382 } |
385 return false; | 383 return false; |
386 } | 384 } |
387 | 385 |
388 bool visitOneShotInterceptor(HOneShotInterceptor node) { | 386 bool visitOneShotInterceptor(HOneShotInterceptor node) { |
389 HInstruction constant = tryComputeConstantInterceptor( | 387 HInstruction constant = |
390 node.inputs[1], node.interceptedClasses); | 388 tryComputeConstantInterceptor(node.inputs[1], node.interceptedClasses); |
391 | 389 |
392 if (constant == null) return false; | 390 if (constant == null) return false; |
393 | 391 |
394 Selector selector = node.selector; | 392 Selector selector = node.selector; |
395 TypeMask mask = node.mask; | 393 TypeMask mask = node.mask; |
396 HInstruction instruction; | 394 HInstruction instruction; |
397 if (selector.isGetter) { | 395 if (selector.isGetter) { |
398 instruction = new HInvokeDynamicGetter( | 396 instruction = new HInvokeDynamicGetter(selector, mask, node.element, |
399 selector, | 397 <HInstruction>[constant, node.inputs[1]], node.instructionType); |
400 mask, | |
401 node.element, | |
402 <HInstruction>[constant, node.inputs[1]], | |
403 node.instructionType); | |
404 } else if (selector.isSetter) { | 398 } else if (selector.isSetter) { |
405 instruction = new HInvokeDynamicSetter( | 399 instruction = new HInvokeDynamicSetter( |
406 selector, | 400 selector, |
407 mask, | 401 mask, |
408 node.element, | 402 node.element, |
409 <HInstruction>[constant, node.inputs[1], node.inputs[2]], | 403 <HInstruction>[constant, node.inputs[1], node.inputs[2]], |
410 node.instructionType); | 404 node.instructionType); |
411 } else { | 405 } else { |
412 List<HInstruction> inputs = new List<HInstruction>.from(node.inputs); | 406 List<HInstruction> inputs = new List<HInstruction>.from(node.inputs); |
413 inputs[0] = constant; | 407 inputs[0] = constant; |
414 instruction = new HInvokeDynamicMethod( | 408 instruction = new HInvokeDynamicMethod( |
415 selector, mask, inputs, node.instructionType, true); | 409 selector, mask, inputs, node.instructionType, true); |
416 } | 410 } |
417 | 411 |
418 HBasicBlock block = node.block; | 412 HBasicBlock block = node.block; |
419 block.addAfter(node, instruction); | 413 block.addAfter(node, instruction); |
420 block.rewrite(node, instruction); | 414 block.rewrite(node, instruction); |
421 return true; | 415 return true; |
422 } | 416 } |
423 } | 417 } |
OLD | NEW |