mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
SILGen: Emit a closure literal in a function conversion as the converted type.
Closure literals are sometimes type-checked as one type then immediately converted to another type in the AST. One particular case of this is when a closure body never throws, but the closure is used as an argument to a function that takes a parameter that `throws`. Emitting this naively, by emitting the closure as its original type, then converting to throws, can be expensive for async closures, since that takes a reabstraction thunk. Even for non-async functions, we still want to get the benefit of reabstraction optimization for the closure literal through the conversion too. So if the function conversion just add `throws`, emit the closure as throwing, and pass down the context abstraction pattern when emitting the closure as well.
This commit is contained in:
@@ -1714,6 +1714,26 @@ static ManagedValue convertFunctionRepresentation(SILGenFunction &SGF,
|
||||
llvm_unreachable("bad representation");
|
||||
}
|
||||
|
||||
// Ideally our prolog/epilog emission would be able to handle all possible
|
||||
// reabstractions and conversions. Until then, this returns true if a closure
|
||||
// literal of type `literalType` can be directly emitted by SILGen as
|
||||
// `convertedType`.
|
||||
static bool canPeepholeLiteralClosureConversion(Type literalType,
|
||||
Type convertedType) {
|
||||
auto literalFnType = literalType->getAs<FunctionType>();
|
||||
auto convertedFnType = convertedType->getAs<FunctionType>();
|
||||
|
||||
if (!literalFnType || !convertedFnType)
|
||||
return false;
|
||||
|
||||
// Does the conversion only add `throws`?
|
||||
if (literalFnType->isEqual(convertedFnType->getWithoutThrowing())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
RValue RValueEmitter::visitFunctionConversionExpr(FunctionConversionExpr *e,
|
||||
SGFContext C)
|
||||
{
|
||||
@@ -1748,6 +1768,38 @@ RValue RValueEmitter::visitFunctionConversionExpr(FunctionConversionExpr *e,
|
||||
return RValue(SGF, e, result);
|
||||
}
|
||||
|
||||
// If the function being converted is a closure literal, then the only use
|
||||
// of the closure should be as the destination type of the conversion. Rather
|
||||
// than emit the closure as is and convert it, see if we can emit the closure
|
||||
// directly as the desired type.
|
||||
//
|
||||
// TODO: Move this up when we can emit closures directly with C calling
|
||||
// convention.
|
||||
auto subExpr = e->getSubExpr()->getSemanticsProvidingExpr();
|
||||
if (isa<AbstractClosureExpr>(subExpr)
|
||||
&& canPeepholeLiteralClosureConversion(subExpr->getType(),
|
||||
e->getType())) {
|
||||
// If we're emitting into a context with a preferred abstraction pattern
|
||||
// already, carry that along.
|
||||
auto origType = C.getAbstractionPattern();
|
||||
// If not, use the conversion type as the desired abstraction pattern.
|
||||
if (!origType) {
|
||||
origType = AbstractionPattern(e->getType()->getCanonicalType());
|
||||
}
|
||||
|
||||
auto substType = subExpr->getType()->getCanonicalType();
|
||||
|
||||
auto conversion = Conversion::getSubstToOrig(*origType, substType,
|
||||
SGF.getLoweredType(*origType, substType));
|
||||
ConvertingInitialization convertingInit(conversion, SGFContext());
|
||||
auto closure = SGF.emitRValue(subExpr,
|
||||
SGFContext(&convertingInit))
|
||||
.getAsSingleValue(SGF, e);
|
||||
closure = SGF.emitSubstToOrigValue(e, closure, *origType, substType);
|
||||
|
||||
return RValue(SGF, e, closure);
|
||||
}
|
||||
|
||||
// Handle a reference to a "thin" native Swift function that only changes
|
||||
// representation and refers to an inherently thin function reference.
|
||||
if (destRepTy->getRepresentation() == FunctionTypeRepresentation::Thin) {
|
||||
|
||||
@@ -552,13 +552,12 @@ func testTryApply(_ x: Float) -> Float {
|
||||
// CHECK: bb0:
|
||||
// CHECK: [ACTIVE] %0 = argument of bb0 : $Float
|
||||
// CHECK: [NONE] // function_ref closure #1 in testTryApply(_:)
|
||||
// CHECK: [NONE] %3 = thin_to_thick_function %2 : $@convention(thin) () -> () to $@noescape @callee_guaranteed () -> ()
|
||||
// CHECK: [NONE] %4 = convert_function %3 : $@noescape @callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> @error Error
|
||||
// CHECK: [NONE] %3 = thin_to_thick_function %2 : $@convention(thin) () -> @error Error to $@noescape @callee_guaranteed () -> @error Error
|
||||
// CHECK: [NONE] // function_ref rethrowing(_:)
|
||||
// CHECK: bb1:
|
||||
// CHECK: [NONE] %7 = argument of bb1 : $()
|
||||
// CHECK: [NONE] %6 = argument of bb1 : $()
|
||||
// CHECK: bb2:
|
||||
// CHECK: [NONE] %9 = argument of bb2 : $Error
|
||||
// CHECK: [NONE] %8 = argument of bb2 : $Error
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Coroutine differentiation (`begin_apply`)
|
||||
|
||||
@@ -35,7 +35,7 @@ public func mangle(s: [UnicodeScalar]) -> [UnicodeScalar] {
|
||||
// Do we care to expose these via lldb?
|
||||
|
||||
// CHECK: define {{.*}}@"$s11patternvars6mangle1sSayAA13UnicodeScalarVGAF_tFA2EXEfU_"
|
||||
// CHECK: %[[VAL:[0-9]+]] = call swiftcc i32 @"$s11patternvars13UnicodeScalarV5values6UInt32Vvg"(i32 %0)
|
||||
// CHECK: %[[VAL:[0-9]+]] = call swiftcc i32 @"$s11patternvars13UnicodeScalarV5values6UInt32Vvg"(
|
||||
// CHECK: {{[0-9]+}}:
|
||||
// CHECK-NOT: call void @llvm.dbg.value
|
||||
// CHECK-NOT: call void asm sideeffect "", "r"
|
||||
|
||||
@@ -203,7 +203,7 @@ public func testGetFunc() {
|
||||
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} hidden swiftcc void @"$s22big_types_corner_cases7TestBigC4testyyF"(%T22big_types_corner_cases7TestBigC* swiftself %0)
|
||||
// CHECK: [[CALL1:%.*]] = call {{.*}} @__swift_instantiateConcreteTypeFromMangledName({{.*}} @"$sSayy22big_types_corner_cases9BigStructVcSgGMD"
|
||||
// CHECK: [[CALL2:%.*]] = call i8** @"$sSayy22big_types_corner_cases9BigStructVcSgGSayxGSlsWl
|
||||
// CHECK: call swiftcc void @"$sSlsE10firstIndex5where0B0QzSgSb7ElementQzKXE_tKF"(%swift.opaque* noalias nocapture sret({{.*}}) %{{[0-9]+}}, i8* bitcast ({{.*}}* @"$s22big_types_corner_cases9BigStruct{{.*}}_TRTA{{(\.ptrauth)?}}" to i8*), %swift.opaque* %{{[0-9]+}}, %swift.type* %{{[0-9]+}}, i8** [[CALL2]]
|
||||
// CHECK: call swiftcc void @"$sSlsE10firstIndex5where0B0QzSgSb7ElementQzKXE_tKF"(%swift.opaque* noalias nocapture sret({{.*}}) %{{[0-9]+}}, i8* bitcast ({{.*}}* @"$s22big_types_corner_cases7TestBig{{.*}}" to i8*), %swift.opaque* null, %swift.type* %{{[0-9]+}}, i8** [[CALL2]]
|
||||
class TestBig {
|
||||
typealias Handler = (BigStruct) -> Void
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
// CHECK-LABEL: closure #1 (Swift.Never) -> Swift.Never in coverage_closure_returns_never.closure_with_fatal_error(Swift.Array<Swift.Never>) -> ()
|
||||
// CHECK: builtin "int_instrprof_increment"
|
||||
// CHECK-NEXT: debug_value {{.*}} : $Never
|
||||
// CHECK-NEXT: [[LOAD:%.*]] = load {{.*}} : $*Never
|
||||
// CHECK-NEXT: debug_value [[LOAD]] : $Never
|
||||
// CHECK-NEXT: unreachable
|
||||
|
||||
func closure_with_fatal_error(_ arr: [Never]) {
|
||||
|
||||
16
test/SILGen/closure_literal_reabstraction_async.swift
Normal file
16
test/SILGen/closure_literal_reabstraction_async.swift
Normal file
@@ -0,0 +1,16 @@
|
||||
// RUN: %target-swift-frontend -emit-silgen %s -disable-availability-checking | %FileCheck %s
|
||||
// REQUIRES: concurrency
|
||||
|
||||
@_silgen_name("takeThrowingAsyncClosure")
|
||||
func takeThrowingAsyncClosure<T>(_: () async throws -> T)
|
||||
|
||||
// CHECK-LABEL: sil {{.*}} @{{.*}}34passNonthrowingAsyncClosureLiteral
|
||||
func passNonthrowingAsyncClosureLiteral() {
|
||||
// Check that the literal closure was emitted directly with an error return,
|
||||
// without a reabstraction thunk to convert from nonthrowing.
|
||||
// CHECK: [[INVOKE_FN:%.*]] = function_ref
|
||||
// CHECK: [[CLOSURE:%.*]] = thin_to_thick_function [[INVOKE_FN]]
|
||||
// CHECK: [[CALLEE:%.*]] = function_ref @takeThrowingAsyncClosure
|
||||
// CHECK: apply [[CALLEE]]<Int>([[CLOSURE]])
|
||||
takeThrowingAsyncClosure { return 42 }
|
||||
}
|
||||
@@ -70,11 +70,10 @@ func test2() {
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil hidden @$s8rethrows5test3yyF : $@convention(thin) () -> () {
|
||||
// CHECK: [[CLOSURE:%.*]] = function_ref @$s8rethrows5test3yyFSiyXEfU_ : $@convention(thin) () -> Int
|
||||
// CHECK: [[CLOSURE:%.*]] = function_ref @$s8rethrows5test3yyFSiyXEfU_ :
|
||||
// CHECK: [[T0:%.*]] = thin_to_thick_function [[CLOSURE]]
|
||||
// CHECK: [[T1:%.*]] = convert_function [[T0]] : $@noescape @callee_guaranteed () -> Int to $@noescape @callee_guaranteed () -> (Int, @error Error)
|
||||
// CHECK: [[RETHROWER:%.*]] = function_ref @$s8rethrows9rethroweryS2iyKXEKF : $@convention(thin) (@noescape @callee_guaranteed () -> (Int, @error Error)) -> (Int, @error Error)
|
||||
// CHECK: try_apply [[RETHROWER]]([[T1]]) : $@convention(thin) (@noescape @callee_guaranteed () -> (Int, @error Error)) -> (Int, @error Error), normal [[NORMAL:bb1]], error [[ERROR:bb2]]
|
||||
// CHECK: [[RETHROWER:%.*]] = function_ref @$s8rethrows9rethroweryS2iyKXEKF :
|
||||
// CHECK: try_apply [[RETHROWER]]([[T0]])
|
||||
// CHECK: [[NORMAL]]({{%.*}} : $Int):
|
||||
// CHECK-NEXT: [[RESULT:%.*]] = tuple ()
|
||||
// CHECK-NEXT: return [[RESULT]]
|
||||
|
||||
@@ -64,7 +64,8 @@ struct S4<Base> where Base : P1, Base.Element: P1 {
|
||||
// CHECK-LABEL: {{^}}sil {{.*}} @${{.*}}2S4{{.*}}3foo{{.*}}F :
|
||||
// CHECK: @callee_guaranteed @substituted <τ_0_0, τ_0_1 where τ_0_0 : P1, τ_0_0 == τ_0_1, τ_0_0.Element : P1> (@in_guaranteed S3<τ_0_0>) -> () for <Base, Base>
|
||||
func foo(index: S3<Base>?) {
|
||||
_ = index.map({ _ = $0 })
|
||||
let f: (S3<Base>) -> () = { _ = $0 }
|
||||
_ = index.map(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user