| 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'; |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 121 List<TypeMask> arguments) { | 121 List<TypeMask> arguments) { |
| 122 _putInternal(element, callStructure, receiver, arguments, false, null); | 122 _putInternal(element, callStructure, receiver, arguments, false, null); |
| 123 } | 123 } |
| 124 | 124 |
| 125 /// Look up a tuple in the cache. | 125 /// Look up a tuple in the cache. |
| 126 /// | 126 /// |
| 127 /// A positive lookup result return the IR function definition. A negative | 127 /// A positive lookup result return the IR function definition. A negative |
| 128 /// lookup result returns [NO_INLINE]. If there is no cached result, | 128 /// lookup result returns [NO_INLINE]. If there is no cached result, |
| 129 /// [ABSENT] is returned. | 129 /// [ABSENT] is returned. |
| 130 get(ExecutableElement element, CallStructure callStructure, TypeMask receiver, | 130 get(ExecutableElement element, CallStructure callStructure, TypeMask receiver, |
| 131 List<TypeMask> arguments) { | 131 List<TypeMask> arguments, DartType concreteType) { |
| 132 List<CacheEntry> entries = map[element]; | 132 List<CacheEntry> entries = map[element]; |
| 133 if (entries != null) { | 133 if (entries != null) { |
| 134 for (CacheEntry entry in entries) { | 134 for (CacheEntry entry in entries) { |
| 135 if (entry.match(callStructure, receiver, arguments)) { | 135 if (entry.match(callStructure, receiver, arguments)) { |
| 136 if (entry.decision) { | 136 if (entry.decision) { |
| 137 FunctionDefinition function = copier.copy(entry.function); | 137 FunctionDefinition function = copier.copy(entry.function, |
| 138 concreteType: concreteType); |
| 138 ParentVisitor.setParents(function); | 139 ParentVisitor.setParents(function); |
| 139 return function; | 140 return function; |
| 140 } | 141 } |
| 141 return NO_INLINE; | 142 return NO_INLINE; |
| 142 } | 143 } |
| 143 } | 144 } |
| 144 } | 145 } |
| 145 return ABSENT; | 146 return ABSENT; |
| 146 } | 147 } |
| 147 } | 148 } |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 352 | 353 |
| 353 // Given an invocation and a known target, possibly perform inlining. | 354 // Given an invocation and a known target, possibly perform inlining. |
| 354 // | 355 // |
| 355 // An optional call structure indicates a dynamic call. Calls that are | 356 // An optional call structure indicates a dynamic call. Calls that are |
| 356 // already resolved statically have a null call structure. | 357 // already resolved statically have a null call structure. |
| 357 // | 358 // |
| 358 // The [Primitive] representing the result of the inlined call is returned | 359 // The [Primitive] representing the result of the inlined call is returned |
| 359 // if the call was inlined, and the inlined function body is available in | 360 // if the call was inlined, and the inlined function body is available in |
| 360 // [_fragment]. If the call was not inlined, null is returned. | 361 // [_fragment]. If the call was not inlined, null is returned. |
| 361 Primitive tryInlining(InvocationPrimitive invoke, FunctionElement target, | 362 Primitive tryInlining(InvocationPrimitive invoke, FunctionElement target, |
| 362 CallStructure callStructure) { | 363 CallStructure callStructure, |
| 364 {GenericType concreteType}) { |
| 363 // Quick checks: do not inline or even cache calls to targets without an | 365 // Quick checks: do not inline or even cache calls to targets without an |
| 364 // AST node, targets that are asynchronous or generator functions, or | 366 // AST node, targets that are asynchronous or generator functions, or |
| 365 // targets containing a try statement. | 367 // targets containing a try statement. |
| 366 if (!target.hasNode) return null; | 368 if (!target.hasNode) return null; |
| 367 if (target.asyncMarker != AsyncMarker.SYNC) return null; | 369 if (target.asyncMarker != AsyncMarker.SYNC) return null; |
| 368 // V8 does not optimize functions containing a try statement. Inlining | 370 // V8 does not optimize functions containing a try statement. Inlining |
| 369 // code containing a try statement will make the optimizable calling code | 371 // code containing a try statement will make the optimizable calling code |
| 370 // become unoptimizable. | 372 // become unoptimizable. |
| 371 if (target.resolvedAst.elements.containsTryStatement) { | 373 if (target.resolvedAst.elements.containsTryStatement) { |
| 372 return null; | 374 return null; |
| 373 } | 375 } |
| 374 | 376 |
| 375 Reference<Primitive> dartReceiver = invoke.dartReceiverReference; | 377 Reference<Primitive> dartReceiver = invoke.dartReceiverReference; |
| 376 TypeMask abstractReceiver = | 378 TypeMask abstractReceiver = |
| 377 dartReceiver == null ? null : abstractType(dartReceiver); | 379 dartReceiver == null ? null : abstractType(dartReceiver); |
| 378 // The receiver is non-null in a method body, unless the receiver is known | 380 // The receiver is non-null in a method body, unless the receiver is known |
| 379 // to be `null` (isEmpty covers `null` and unreachable). | 381 // to be `null` (isEmpty covers `null` and unreachable). |
| 380 TypeMask abstractReceiverInMethod = abstractReceiver == null | 382 TypeMask abstractReceiverInMethod = abstractReceiver == null |
| 381 ? null | 383 ? null |
| 382 : abstractReceiver.isEmpty | 384 : abstractReceiver.isEmpty |
| 383 ? abstractReceiver | 385 ? abstractReceiver |
| 384 : abstractReceiver.nonNullable(); | 386 : abstractReceiver.nonNullable(); |
| 385 List<TypeMask> abstractArguments = | 387 List<TypeMask> abstractArguments = |
| 386 invoke.arguments.map(abstractType).toList(); | 388 invoke.arguments.map(abstractType).toList(); |
| 387 var cachedResult = _inliner.cache.get(target, callStructure, | 389 var cachedResult = _inliner.cache.get(target, callStructure, |
| 388 abstractReceiverInMethod, | 390 abstractReceiverInMethod, |
| 389 abstractArguments); | 391 abstractArguments, |
| 392 concreteType); |
| 390 | 393 |
| 391 // Negative inlining result in the cache. | 394 // Negative inlining result in the cache. |
| 392 if (cachedResult == InliningCache.NO_INLINE) return null; | 395 if (cachedResult == InliningCache.NO_INLINE) return null; |
| 393 | 396 |
| 394 Primitive finish(FunctionDefinition function) { | 397 Primitive finish(FunctionDefinition function) { |
| 395 _fragment = new CpsFragment(invoke.sourceInformation); | 398 _fragment = new CpsFragment(invoke.sourceInformation); |
| 396 Primitive receiver = invoke.receiver?.definition; | 399 Primitive receiver = invoke.receiver?.definition; |
| 397 List<Primitive> arguments = | 400 List<Primitive> arguments = |
| 398 invoke.arguments.map((Reference ref) => ref.definition).toList(); | 401 invoke.arguments.map((Reference ref) => ref.definition).toList(); |
| 399 // Add a null check to the inlined function body if necessary. The | 402 // Add a null check to the inlined function body if necessary. The |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 516 | 519 |
| 517 @override | 520 @override |
| 518 Primitive visitInvokeMethodDirectly(InvokeMethodDirectly node) { | 521 Primitive visitInvokeMethodDirectly(InvokeMethodDirectly node) { |
| 519 if (node.selector.isGetter != node.target.isGetter) return null; | 522 if (node.selector.isGetter != node.target.isGetter) return null; |
| 520 if (node.selector.isSetter != node.target.isSetter) return null; | 523 if (node.selector.isSetter != node.target.isSetter) return null; |
| 521 return tryInlining(node, node.target, null); | 524 return tryInlining(node, node.target, null); |
| 522 } | 525 } |
| 523 | 526 |
| 524 @override | 527 @override |
| 525 Primitive visitInvokeConstructor(InvokeConstructor node) { | 528 Primitive visitInvokeConstructor(InvokeConstructor node) { |
| 526 if (node.dartType is GenericType) { | 529 return tryInlining(node, node.target, null, concreteType: node.dartType); |
| 527 // We cannot inline a constructor invocation containing type arguments | |
| 528 // because CreateInstance in the body does not know the type arguments. | |
| 529 // We would incorrectly instantiate a class like A instead of A<B>. | |
| 530 // TODO(kmillikin): try to fix this. | |
| 531 GenericType generic = node.dartType; | |
| 532 if (generic.typeArguments.any((DartType t) => !t.isDynamic)) return null; | |
| 533 } | |
| 534 return tryInlining(node, node.target, null); | |
| 535 } | 530 } |
| 536 } | 531 } |
| OLD | NEW |