| 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 cps_ir.optimization.inline; | 5 library cps_ir.optimization.inline; |
| 6 | 6 |
| 7 import 'cps_fragment.dart'; | 7 import 'cps_fragment.dart'; |
| 8 import 'cps_ir_builder.dart' show ThisParameterLocal; | 8 import 'cps_ir_builder.dart' show ThisParameterLocal; |
| 9 import 'cps_ir_nodes.dart'; | 9 import 'cps_ir_nodes.dart'; |
| 10 import 'optimizers.dart'; | 10 import 'optimizers.dart'; |
| 11 import 'type_mask_system.dart' show TypeMaskSystem; | 11 import 'type_mask_system.dart' show TypeMaskSystem; |
| 12 import '../dart_types.dart' show DartType, GenericType; | 12 import '../dart_types.dart' show DartType, GenericType; |
| 13 import '../world.dart' show World; | 13 import '../world.dart' show World; |
| 14 import '../elements/elements.dart'; | 14 import '../elements/elements.dart'; |
| 15 import '../js_backend/js_backend.dart' show JavaScriptBackend; | 15 import '../js_backend/js_backend.dart' show JavaScriptBackend; |
| 16 import '../js_backend/codegen/task.dart' show CpsFunctionCompiler; | 16 import '../js_backend/codegen/task.dart' show CpsFunctionCompiler; |
| 17 import '../types/types.dart' show | 17 import '../types/types.dart' |
| 18 FlatTypeMask, ForwardingTypeMask, TypeMask, UnionTypeMask; | 18 show FlatTypeMask, ForwardingTypeMask, TypeMask, UnionTypeMask; |
| 19 import '../universe/call_structure.dart' show CallStructure; | 19 import '../universe/call_structure.dart' show CallStructure; |
| 20 import '../universe/selector.dart' show Selector; | 20 import '../universe/selector.dart' show Selector; |
| 21 import 'package:js_ast/js_ast.dart' as js; | 21 import 'package:js_ast/js_ast.dart' as js; |
| 22 | 22 |
| 23 /// Inlining stack entries. | 23 /// Inlining stack entries. |
| 24 /// | 24 /// |
| 25 /// During inlining, a stack is used to detect cycles in the call graph. | 25 /// During inlining, a stack is used to detect cycles in the call graph. |
| 26 class StackEntry { | 26 class StackEntry { |
| 27 // Dynamically resolved calls might be targeting an adapter function that | 27 // Dynamically resolved calls might be targeting an adapter function that |
| 28 // fills in optional arguments not passed at the call site. Therefore these | 28 // fills in optional arguments not passed at the call site. Therefore these |
| (...skipping 26 matching lines...) Expand all Loading... |
| 55 final FunctionDefinition function; | 55 final FunctionDefinition function; |
| 56 | 56 |
| 57 CacheEntry(this.callStructure, this.receiver, this.arguments, this.decision, | 57 CacheEntry(this.callStructure, this.receiver, this.arguments, this.decision, |
| 58 this.function); | 58 this.function); |
| 59 | 59 |
| 60 bool match(CallStructure otherCallStructure, TypeMask otherReceiver, | 60 bool match(CallStructure otherCallStructure, TypeMask otherReceiver, |
| 61 List<TypeMask> otherArguments) { | 61 List<TypeMask> otherArguments) { |
| 62 if (callStructure == null) { | 62 if (callStructure == null) { |
| 63 if (otherCallStructure != null) return false; | 63 if (otherCallStructure != null) return false; |
| 64 } else if (otherCallStructure == null || | 64 } else if (otherCallStructure == null || |
| 65 !callStructure.match(otherCallStructure)) { | 65 !callStructure.match(otherCallStructure)) { |
| 66 return false; | 66 return false; |
| 67 } | 67 } |
| 68 | 68 |
| 69 if (receiver != otherReceiver) return false; | 69 if (receiver != otherReceiver) return false; |
| 70 assert(arguments.length == otherArguments.length); | 70 assert(arguments.length == otherArguments.length); |
| 71 for (int i = 0; i < arguments.length; ++i) { | 71 for (int i = 0; i < arguments.length; ++i) { |
| 72 if (arguments[i] != otherArguments[i]) return false; | 72 if (arguments[i] != otherArguments[i]) return false; |
| 73 } | 73 } |
| 74 return true; | 74 return true; |
| 75 } | 75 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 90 final Map<ExecutableElement, FunctionDefinition> unoptimized = | 90 final Map<ExecutableElement, FunctionDefinition> unoptimized = |
| 91 <ExecutableElement, FunctionDefinition>{}; | 91 <ExecutableElement, FunctionDefinition>{}; |
| 92 | 92 |
| 93 final Map<ExecutableElement, List<CacheEntry>> map = | 93 final Map<ExecutableElement, List<CacheEntry>> map = |
| 94 <ExecutableElement, List<CacheEntry>>{}; | 94 <ExecutableElement, List<CacheEntry>>{}; |
| 95 | 95 |
| 96 // When function definitions are put into or removed from the cache, they are | 96 // When function definitions are put into or removed from the cache, they are |
| 97 // copied because the compiler passes will mutate them. | 97 // copied because the compiler passes will mutate them. |
| 98 final CopyingVisitor copier = new CopyingVisitor(); | 98 final CopyingVisitor copier = new CopyingVisitor(); |
| 99 | 99 |
| 100 void _putInternal(ExecutableElement element, CallStructure callStructure, | 100 void _putInternal( |
| 101 ExecutableElement element, |
| 102 CallStructure callStructure, |
| 101 TypeMask receiver, | 103 TypeMask receiver, |
| 102 List<TypeMask> arguments, | 104 List<TypeMask> arguments, |
| 103 bool decision, | 105 bool decision, |
| 104 FunctionDefinition function) { | 106 FunctionDefinition function) { |
| 105 map.putIfAbsent(element, () => <CacheEntry>[]) | 107 map.putIfAbsent(element, () => <CacheEntry>[]).add( |
| 106 .add(new CacheEntry(callStructure, receiver, arguments, decision, | 108 new CacheEntry(callStructure, receiver, arguments, decision, function)); |
| 107 function)); | |
| 108 } | 109 } |
| 109 | 110 |
| 110 /// Put a positive inlining decision in the cache. | 111 /// Put a positive inlining decision in the cache. |
| 111 /// | 112 /// |
| 112 /// A positive inlining decision maps to an IR function definition. | 113 /// A positive inlining decision maps to an IR function definition. |
| 113 void putPositive(ExecutableElement element, CallStructure callStructure, | 114 void putPositive( |
| 115 ExecutableElement element, |
| 116 CallStructure callStructure, |
| 114 TypeMask receiver, | 117 TypeMask receiver, |
| 115 List<TypeMask> arguments, | 118 List<TypeMask> arguments, |
| 116 FunctionDefinition function) { | 119 FunctionDefinition function) { |
| 117 _putInternal(element, callStructure, receiver, arguments, true, | 120 _putInternal(element, callStructure, receiver, arguments, true, |
| 118 copier.copy(function)); | 121 copier.copy(function)); |
| 119 } | 122 } |
| 120 | 123 |
| 121 /// Put a negative inlining decision in the cache. | 124 /// Put a negative inlining decision in the cache. |
| 122 void putNegative(ExecutableElement element, | 125 void putNegative(ExecutableElement element, CallStructure callStructure, |
| 123 CallStructure callStructure, | 126 TypeMask receiver, List<TypeMask> arguments) { |
| 124 TypeMask receiver, | |
| 125 List<TypeMask> arguments) { | |
| 126 _putInternal(element, callStructure, receiver, arguments, false, null); | 127 _putInternal(element, callStructure, receiver, arguments, false, null); |
| 127 } | 128 } |
| 128 | 129 |
| 129 /// Look up a tuple in the cache. | 130 /// Look up a tuple in the cache. |
| 130 /// | 131 /// |
| 131 /// A positive lookup result return the IR function definition. A negative | 132 /// A positive lookup result return the IR function definition. A negative |
| 132 /// lookup result returns [NO_INLINE]. If there is no cached result, | 133 /// lookup result returns [NO_INLINE]. If there is no cached result, |
| 133 /// [ABSENT] is returned. | 134 /// [ABSENT] is returned. |
| 134 get(ExecutableElement element, CallStructure callStructure, TypeMask receiver, | 135 get(ExecutableElement element, CallStructure callStructure, TypeMask receiver, |
| 135 List<TypeMask> arguments) { | 136 List<TypeMask> arguments) { |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 181 final InliningCache cache = new InliningCache(); | 182 final InliningCache cache = new InliningCache(); |
| 182 | 183 |
| 183 final List<StackEntry> stack = <StackEntry>[]; | 184 final List<StackEntry> stack = <StackEntry>[]; |
| 184 | 185 |
| 185 Inliner(this.functionCompiler); | 186 Inliner(this.functionCompiler); |
| 186 | 187 |
| 187 bool isCalledOnce(Element element) { | 188 bool isCalledOnce(Element element) { |
| 188 if (element is ConstructorBodyElement) { | 189 if (element is ConstructorBodyElement) { |
| 189 ClassElement class_ = element.enclosingClass; | 190 ClassElement class_ = element.enclosingClass; |
| 190 return !functionCompiler.compiler.world.hasAnyStrictSubclass(class_) && | 191 return !functionCompiler.compiler.world.hasAnyStrictSubclass(class_) && |
| 191 class_.constructors.tail?.isEmpty ?? false; | 192 class_.constructors.tail?.isEmpty ?? |
| 193 false; |
| 192 } | 194 } |
| 193 return functionCompiler.compiler.typesTask.typesInferrer.isCalledOnce( | 195 return functionCompiler.compiler.typesTask.typesInferrer |
| 194 element); | 196 .isCalledOnce(element); |
| 195 } | 197 } |
| 196 | 198 |
| 197 void rewrite(FunctionDefinition node, [CallStructure callStructure]) { | 199 void rewrite(FunctionDefinition node, [CallStructure callStructure]) { |
| 198 ExecutableElement function = node.element; | 200 ExecutableElement function = node.element; |
| 199 | 201 |
| 200 // Inlining in asynchronous or generator functions is disabled. Inlining | 202 // Inlining in asynchronous or generator functions is disabled. Inlining |
| 201 // triggers a bug in the async rewriter. | 203 // triggers a bug in the async rewriter. |
| 202 // TODO(kmillikin): Fix the bug and eliminate this restriction if it makes | 204 // TODO(kmillikin): Fix the bug and eliminate this restriction if it makes |
| 203 // sense. | 205 // sense. |
| 204 if (function is FunctionElement && | 206 if (function is FunctionElement && |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 } | 339 } |
| 338 | 340 |
| 339 TypeMask abstractType(Primitive def) { | 341 TypeMask abstractType(Primitive def) { |
| 340 return def.type ?? typeSystem.dynamicType; | 342 return def.type ?? typeSystem.dynamicType; |
| 341 } | 343 } |
| 342 | 344 |
| 343 /// Build the IR term for the function that adapts a call site targeting a | 345 /// Build the IR term for the function that adapts a call site targeting a |
| 344 /// function that takes optional arguments not passed at the call site. | 346 /// function that takes optional arguments not passed at the call site. |
| 345 FunctionDefinition buildAdapter(InvokeMethod node, FunctionElement target) { | 347 FunctionDefinition buildAdapter(InvokeMethod node, FunctionElement target) { |
| 346 Parameter thisParameter = new Parameter(new ThisParameterLocal(target)) | 348 Parameter thisParameter = new Parameter(new ThisParameterLocal(target)) |
| 347 ..type = node.receiver.type; | 349 ..type = node.receiver.type; |
| 348 Parameter interceptorParameter = node.interceptorRef != null | 350 Parameter interceptorParameter = |
| 349 ? new Parameter(null) | 351 node.interceptorRef != null ? new Parameter(null) : null; |
| 350 : null; | 352 List<Parameter> parameters = |
| 351 List<Parameter> parameters = new List<Parameter>.generate( | 353 new List<Parameter>.generate(node.argumentRefs.length, (int index) { |
| 352 node.argumentRefs.length, | 354 // TODO(kmillikin): Use a hint for the parameter names. |
| 353 (int index) { | 355 return new Parameter(null)..type = node.argument(index).type; |
| 354 // TODO(kmillikin): Use a hint for the parameter names. | 356 }); |
| 355 return new Parameter(null) | |
| 356 ..type = node.argument(index).type; | |
| 357 }); | |
| 358 Continuation returnContinuation = new Continuation.retrn(); | 357 Continuation returnContinuation = new Continuation.retrn(); |
| 359 CpsFragment cps = new CpsFragment(); | 358 CpsFragment cps = new CpsFragment(); |
| 360 | 359 |
| 361 FunctionSignature signature = target.functionSignature; | 360 FunctionSignature signature = target.functionSignature; |
| 362 int requiredParameterCount = signature.requiredParameterCount; | 361 int requiredParameterCount = signature.requiredParameterCount; |
| 363 List<Primitive> arguments = new List<Primitive>.generate( | 362 List<Primitive> arguments = new List<Primitive>.generate( |
| 364 requiredParameterCount, | 363 requiredParameterCount, (int index) => parameters[index]); |
| 365 (int index) => parameters[index]); | |
| 366 | 364 |
| 367 int parameterIndex = requiredParameterCount; | 365 int parameterIndex = requiredParameterCount; |
| 368 CallStructure newCallStructure; | 366 CallStructure newCallStructure; |
| 369 if (signature.optionalParametersAreNamed) { | 367 if (signature.optionalParametersAreNamed) { |
| 370 List<String> incomingNames = | 368 List<String> incomingNames = |
| 371 node.selector.callStructure.getOrderedNamedArguments(); | 369 node.selector.callStructure.getOrderedNamedArguments(); |
| 372 List<String> outgoingNames = <String>[]; | 370 List<String> outgoingNames = <String>[]; |
| 373 int nameIndex = 0; | 371 int nameIndex = 0; |
| 374 signature.orderedOptionalParameters.forEach((ParameterElement formal) { | 372 signature.orderedOptionalParameters.forEach((ParameterElement formal) { |
| 375 if (nameIndex < incomingNames.length && | 373 if (nameIndex < incomingNames.length && |
| (...skipping 17 matching lines...) Expand all Loading... |
| 393 } else { | 391 } else { |
| 394 Constant defaultValue = cps.makeConstant( | 392 Constant defaultValue = cps.makeConstant( |
| 395 backend.constants.getConstantValueForVariable(formal)); | 393 backend.constants.getConstantValueForVariable(formal)); |
| 396 defaultValue.type = typeSystem.getParameterType(formal); | 394 defaultValue.type = typeSystem.getParameterType(formal); |
| 397 arguments.add(defaultValue); | 395 arguments.add(defaultValue); |
| 398 } | 396 } |
| 399 }); | 397 }); |
| 400 newCallStructure = new CallStructure(signature.parameterCount); | 398 newCallStructure = new CallStructure(signature.parameterCount); |
| 401 } | 399 } |
| 402 | 400 |
| 403 Selector newSelector = | 401 Selector newSelector = new Selector( |
| 404 new Selector(node.selector.kind, node.selector.memberName, | 402 node.selector.kind, node.selector.memberName, newCallStructure); |
| 405 newCallStructure); | 403 Primitive result = cps.invokeMethod( |
| 406 Primitive result = cps.invokeMethod(thisParameter, | 404 thisParameter, newSelector, node.mask, arguments, |
| 407 newSelector, | |
| 408 node.mask, | |
| 409 arguments, | |
| 410 interceptor: interceptorParameter, | 405 interceptor: interceptorParameter, |
| 411 callingConvention: node.callingConvention); | 406 callingConvention: node.callingConvention); |
| 412 result.type = typeSystem.getInvokeReturnType(node.selector, node.mask); | 407 result.type = typeSystem.getInvokeReturnType(node.selector, node.mask); |
| 413 returnContinuation.parameters.single.type = result.type; | 408 returnContinuation.parameters.single.type = result.type; |
| 414 cps.invokeContinuation(returnContinuation, <Primitive>[result]); | 409 cps.invokeContinuation(returnContinuation, <Primitive>[result]); |
| 415 return new FunctionDefinition(target, thisParameter, parameters, | 410 return new FunctionDefinition( |
| 416 returnContinuation, | 411 target, thisParameter, parameters, returnContinuation, cps.root, |
| 417 cps.root, | |
| 418 interceptorParameter: interceptorParameter); | 412 interceptorParameter: interceptorParameter); |
| 419 } | 413 } |
| 420 | 414 |
| 421 // Given an invocation and a known target, possibly perform inlining. | 415 // Given an invocation and a known target, possibly perform inlining. |
| 422 // | 416 // |
| 423 // An optional call structure indicates a dynamic call. Calls that are | 417 // An optional call structure indicates a dynamic call. Calls that are |
| 424 // already resolved statically have a null call structure. | 418 // already resolved statically have a null call structure. |
| 425 // | 419 // |
| 426 // The [Primitive] representing the result of the inlined call is returned | 420 // The [Primitive] representing the result of the inlined call is returned |
| 427 // if the call was inlined, and the inlined function body is available in | 421 // if the call was inlined, and the inlined function body is available in |
| 428 // [_fragment]. If the call was not inlined, null is returned. | 422 // [_fragment]. If the call was not inlined, null is returned. |
| 429 Primitive tryInlining(InvocationPrimitive invoke, FunctionElement target, | 423 Primitive tryInlining(InvocationPrimitive invoke, FunctionElement target, |
| 430 CallStructure callStructure) { | 424 CallStructure callStructure) { |
| 431 // Quick checks: do not inline or even cache calls to targets without an | 425 // Quick checks: do not inline or even cache calls to targets without an |
| 432 // AST node, targets that are asynchronous or generator functions, or | 426 // AST node, targets that are asynchronous or generator functions, or |
| 433 // targets containing a try statement. | 427 // targets containing a try statement. |
| 434 if (!target.hasNode) return null; | 428 if (!target.hasNode) return null; |
| 435 if (backend.isJsInterop(target)) return null; | 429 if (backend.isJsInterop(target)) return null; |
| 436 if (target.asyncMarker != AsyncMarker.SYNC) return null; | 430 if (target.asyncMarker != AsyncMarker.SYNC) return null; |
| 437 // V8 does not optimize functions containing a try statement. Inlining | 431 // V8 does not optimize functions containing a try statement. Inlining |
| 438 // code containing a try statement will make the optimizable calling code | 432 // code containing a try statement will make the optimizable calling code |
| 439 // become unoptimizable. | 433 // become unoptimizable. |
| 440 if (target.resolvedAst.elements.containsTryStatement) { | 434 if (target.resolvedAst.elements.containsTryStatement) { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 461 receiver == null ? null : abstractType(receiver); | 455 receiver == null ? null : abstractType(receiver); |
| 462 // The receiver is non-null in a method body, unless the receiver is known | 456 // The receiver is non-null in a method body, unless the receiver is known |
| 463 // to be `null` (isEmpty covers `null` and unreachable). | 457 // to be `null` (isEmpty covers `null` and unreachable). |
| 464 TypeMask abstractReceiverInMethod = abstractReceiver == null | 458 TypeMask abstractReceiverInMethod = abstractReceiver == null |
| 465 ? null | 459 ? null |
| 466 : abstractReceiver.isEmptyOrNull | 460 : abstractReceiver.isEmptyOrNull |
| 467 ? abstractReceiver | 461 ? abstractReceiver |
| 468 : abstractReceiver.nonNullable(); | 462 : abstractReceiver.nonNullable(); |
| 469 List<TypeMask> abstractArguments = | 463 List<TypeMask> abstractArguments = |
| 470 invoke.arguments.map(abstractType).toList(); | 464 invoke.arguments.map(abstractType).toList(); |
| 471 var cachedResult = _inliner.cache.get(target, callStructure, | 465 var cachedResult = _inliner.cache.get( |
| 472 abstractReceiverInMethod, | 466 target, callStructure, abstractReceiverInMethod, abstractArguments); |
| 473 abstractArguments); | |
| 474 | 467 |
| 475 // Negative inlining result in the cache. | 468 // Negative inlining result in the cache. |
| 476 if (cachedResult == InliningCache.NO_INLINE) return null; | 469 if (cachedResult == InliningCache.NO_INLINE) return null; |
| 477 | 470 |
| 478 Primitive finish(FunctionDefinition function) { | 471 Primitive finish(FunctionDefinition function) { |
| 479 _fragment = new CpsFragment(invoke.sourceInformation); | 472 _fragment = new CpsFragment(invoke.sourceInformation); |
| 480 Primitive receiver = invoke.receiver; | 473 Primitive receiver = invoke.receiver; |
| 481 List<Primitive> arguments = invoke.arguments.toList(); | 474 List<Primitive> arguments = invoke.arguments.toList(); |
| 482 // Add a null check to the inlined function body if necessary. The | 475 // Add a null check to the inlined function body if necessary. The |
| 483 // cached function body does not contain the null check. | 476 // cached function body does not contain the null check. |
| 484 if (receiver != null && abstractReceiver.isNullable) { | 477 if (receiver != null && abstractReceiver.isNullable) { |
| 485 receiver = nullReceiverGuard( | 478 receiver = |
| 486 invoke, _fragment, receiver, abstractReceiver); | 479 nullReceiverGuard(invoke, _fragment, receiver, abstractReceiver); |
| 487 } | 480 } |
| 488 return _fragment.inlineFunction(function, receiver, arguments, | 481 return _fragment.inlineFunction(function, receiver, arguments, |
| 489 interceptor: invoke.interceptor, hint: invoke.hint); | 482 interceptor: invoke.interceptor, hint: invoke.hint); |
| 490 } | 483 } |
| 491 | 484 |
| 492 // Positive inlining result in the cache. | 485 // Positive inlining result in the cache. |
| 493 if (cachedResult is FunctionDefinition) { | 486 if (cachedResult is FunctionDefinition) { |
| 494 return finish(cachedResult); | 487 return finish(cachedResult); |
| 495 } | 488 } |
| 496 | 489 |
| 497 // We have not seen this combination of target and abstract arguments | 490 // We have not seen this combination of target and abstract arguments |
| 498 // before. Make an inlining decision. | 491 // before. Make an inlining decision. |
| 499 assert(cachedResult == InliningCache.ABSENT); | 492 assert(cachedResult == InliningCache.ABSENT); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 535 // Compute the size. | 528 // Compute the size. |
| 536 // TODO(kmillikin): Tune the size bound. | 529 // TODO(kmillikin): Tune the size bound. |
| 537 int size = SizeVisitor.sizeOf(invoke, function); | 530 int size = SizeVisitor.sizeOf(invoke, function); |
| 538 if (!_inliner.isCalledOnce(target) && size > 11) return doNotInline(); | 531 if (!_inliner.isCalledOnce(target) && size > 11) return doNotInline(); |
| 539 | 532 |
| 540 _inliner.cache.putPositive(target, callStructure, abstractReceiverInMethod, | 533 _inliner.cache.putPositive(target, callStructure, abstractReceiverInMethod, |
| 541 abstractArguments, function); | 534 abstractArguments, function); |
| 542 return finish(function); | 535 return finish(function); |
| 543 } | 536 } |
| 544 | 537 |
| 545 Primitive nullReceiverGuard(InvocationPrimitive invoke, | 538 Primitive nullReceiverGuard(InvocationPrimitive invoke, CpsFragment fragment, |
| 546 CpsFragment fragment, | 539 Primitive dartReceiver, TypeMask abstractReceiver) { |
| 547 Primitive dartReceiver, | |
| 548 TypeMask abstractReceiver) { | |
| 549 if (invoke is! InvokeMethod) return dartReceiver; | 540 if (invoke is! InvokeMethod) return dartReceiver; |
| 550 InvokeMethod invokeMethod = invoke; | 541 InvokeMethod invokeMethod = invoke; |
| 551 Selector selector = invokeMethod.selector; | 542 Selector selector = invokeMethod.selector; |
| 552 if (typeSystem.isDefinitelyNum(abstractReceiver, allowNull: true)) { | 543 if (typeSystem.isDefinitelyNum(abstractReceiver, allowNull: true)) { |
| 553 Primitive condition = _fragment.letPrim( | 544 Primitive condition = _fragment.letPrim(new ApplyBuiltinOperator( |
| 554 new ApplyBuiltinOperator(BuiltinOperator.IsNotNumber, | 545 BuiltinOperator.IsNotNumber, |
| 555 <Primitive>[dartReceiver], | 546 <Primitive>[dartReceiver], |
| 556 invoke.sourceInformation)); | 547 invoke.sourceInformation)); |
| 557 condition.type = typeSystem.boolType; | 548 condition.type = typeSystem.boolType; |
| 558 Primitive check = _fragment.letPrim( | 549 Primitive check = _fragment.letPrim(new ReceiverCheck.nullCheck( |
| 559 new ReceiverCheck.nullCheck(dartReceiver, selector, | 550 dartReceiver, selector, invoke.sourceInformation, |
| 560 invoke.sourceInformation, | 551 condition: condition)); |
| 561 condition: condition)); | |
| 562 check.type = abstractReceiver.nonNullable(); | 552 check.type = abstractReceiver.nonNullable(); |
| 563 return check; | 553 return check; |
| 564 } | 554 } |
| 565 | 555 |
| 566 Primitive check = _fragment.letPrim( | 556 Primitive check = _fragment.letPrim(new ReceiverCheck.nullCheck( |
| 567 new ReceiverCheck.nullCheck(dartReceiver, selector, | 557 dartReceiver, selector, invoke.sourceInformation)); |
| 568 invoke.sourceInformation)); | |
| 569 check.type = abstractReceiver.nonNullable(); | 558 check.type = abstractReceiver.nonNullable(); |
| 570 return check; | 559 return check; |
| 571 } | 560 } |
| 572 | 561 |
| 573 | |
| 574 @override | 562 @override |
| 575 Primitive visitInvokeStatic(InvokeStatic node) { | 563 Primitive visitInvokeStatic(InvokeStatic node) { |
| 576 return tryInlining(node, node.target, null); | 564 return tryInlining(node, node.target, null); |
| 577 } | 565 } |
| 578 | 566 |
| 579 @override | 567 @override |
| 580 Primitive visitInvokeMethod(InvokeMethod node) { | 568 Primitive visitInvokeMethod(InvokeMethod node) { |
| 581 Primitive receiver = node.receiver; | 569 Primitive receiver = node.receiver; |
| 582 Element element = world.locateSingleElement(node.selector, receiver.type); | 570 Element element = world.locateSingleElement(node.selector, receiver.type); |
| 583 if (element == null || element is! FunctionElement) return null; | 571 if (element == null || element is! FunctionElement) return null; |
| 584 if (node.selector.isGetter != element.isGetter) return null; | 572 if (node.selector.isGetter != element.isGetter) return null; |
| 585 if (node.selector.isSetter != element.isSetter) return null; | 573 if (node.selector.isSetter != element.isSetter) return null; |
| 586 if (node.selector.name != element.name) return null; | 574 if (node.selector.name != element.name) return null; |
| 587 | 575 |
| 588 return tryInlining(node, element.asFunctionElement(), | 576 return tryInlining( |
| 589 node.selector.callStructure); | 577 node, element.asFunctionElement(), node.selector.callStructure); |
| 590 } | 578 } |
| 591 | 579 |
| 592 @override | 580 @override |
| 593 Primitive visitInvokeMethodDirectly(InvokeMethodDirectly node) { | 581 Primitive visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
| 594 if (node.selector.isGetter != node.target.isGetter) return null; | 582 if (node.selector.isGetter != node.target.isGetter) return null; |
| 595 if (node.selector.isSetter != node.target.isSetter) return null; | 583 if (node.selector.isSetter != node.target.isSetter) return null; |
| 596 return tryInlining(node, node.target, null); | 584 return tryInlining(node, node.target, null); |
| 597 } | 585 } |
| 598 | 586 |
| 599 @override | 587 @override |
| 600 Primitive visitInvokeConstructor(InvokeConstructor node) { | 588 Primitive visitInvokeConstructor(InvokeConstructor node) { |
| 601 if (node.dartType is GenericType) { | 589 if (node.dartType is GenericType) { |
| 602 // We cannot inline a constructor invocation containing type arguments | 590 // We cannot inline a constructor invocation containing type arguments |
| 603 // because CreateInstance in the body does not know the type arguments. | 591 // because CreateInstance in the body does not know the type arguments. |
| 604 // We would incorrectly instantiate a class like A instead of A<B>. | 592 // We would incorrectly instantiate a class like A instead of A<B>. |
| 605 // TODO(kmillikin): try to fix this. | 593 // TODO(kmillikin): try to fix this. |
| 606 GenericType generic = node.dartType; | 594 GenericType generic = node.dartType; |
| 607 if (generic.typeArguments.any((DartType t) => !t.isDynamic)) return null; | 595 if (generic.typeArguments.any((DartType t) => !t.isDynamic)) return null; |
| 608 } | 596 } |
| 609 return tryInlining(node, node.target, null); | 597 return tryInlining(node, node.target, null); |
| 610 } | 598 } |
| 611 | 599 |
| 612 bool isBlacklisted(FunctionElement target) { | 600 bool isBlacklisted(FunctionElement target) { |
| 613 ClassElement enclosingClass = target.enclosingClass; | 601 ClassElement enclosingClass = target.enclosingClass; |
| 614 if (target.isOperator && | 602 if (target.isOperator && |
| 615 (enclosingClass == backend.helpers.jsNumberClass || | 603 (enclosingClass == backend.helpers.jsNumberClass || |
| 616 enclosingClass == backend.helpers.jsDoubleClass || | 604 enclosingClass == backend.helpers.jsDoubleClass || |
| 617 enclosingClass == backend.helpers.jsIntClass)) { | 605 enclosingClass == backend.helpers.jsIntClass)) { |
| 618 // These should be handled by operator specialization. | 606 // These should be handled by operator specialization. |
| 619 return true; | 607 return true; |
| 620 } | 608 } |
| 621 if (target == backend.helpers.stringInterpolationHelper) return true; | 609 if (target == backend.helpers.stringInterpolationHelper) return true; |
| 622 return false; | 610 return false; |
| 623 } | 611 } |
| 624 } | 612 } |
| OLD | NEW |