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 |