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