Skip to content

Commit 8e2d68c

Browse files
committed
SILGen: Avoid using back deployment thunks for high enough deployment targets.
If a function body references a declaration with the `@_backDeploy(before:)` attribute and that function body will only execute on deployment targets for which the ABI version of the decl is available then it is unnecessary to thunk the reference to the decl. Function bodies that may be emitted into other modules (e.g. `@inlinable`) must always use the thunk. Resolves rdar://90729799
1 parent 6d1876c commit 8e2d68c

14 files changed

+152
-47
lines changed

lib/SILGen/SILGen.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
422422
/// Is the self method of the given nonmutating method passed indirectly?
423423
bool isNonMutatingSelfIndirect(SILDeclRef method);
424424

425-
SILDeclRef getAccessorDeclRef(AccessorDecl *accessor);
425+
SILDeclRef getAccessorDeclRef(AccessorDecl *accessor,
426+
ResilienceExpansion expansion);
426427

427428
bool canStorageUseStoredKeyPathComponent(AbstractStorageDecl *decl,
428429
ResilienceExpansion expansion);

lib/SILGen/SILGenApply.cpp

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,31 @@ static void convertOwnershipConventionsGivenParamInfos(
259259
});
260260
}
261261

262+
static bool shouldApplyBackDeploymentThunk(ValueDecl *decl, ASTContext &ctx,
263+
ResilienceExpansion expansion) {
264+
auto backDeployBeforeVersion = decl->getBackDeployBeforeOSVersion(ctx);
265+
if (!backDeployBeforeVersion)
266+
return false;
267+
268+
// If the context of the application is inlinable then we must always call the
269+
// back deployment thunk since we can't predict the deployment targets of
270+
// other modules.
271+
if (expansion != ResilienceExpansion::Maximal)
272+
return true;
273+
274+
// In resilient function bodies skip calling the back deployment thunk when
275+
// the deployment target is high enough that the ABI implementation of the
276+
// back deployed function is guaranteed to be available.
277+
auto deploymentAvailability = AvailabilityContext::forDeploymentTarget(ctx);
278+
auto declAvailability =
279+
AvailabilityContext(VersionRange::allGTE(*backDeployBeforeVersion));
280+
281+
if (deploymentAvailability.isContainedIn(declAvailability))
282+
return false;
283+
284+
return true;
285+
}
286+
262287
//===----------------------------------------------------------------------===//
263288
// Callee
264289
//===----------------------------------------------------------------------===//
@@ -1104,6 +1129,28 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11041129
}
11051130
}
11061131

1132+
SILDeclRef getDeclRefForStaticDispatchApply(DeclRefExpr *e) {
1133+
auto *afd = cast<AbstractFunctionDecl>(e->getDecl());
1134+
auto &ctx = SGF.getASTContext();
1135+
1136+
// A call to a `distributed` function may need to go through a thunk.
1137+
if (callSite && callSite->shouldApplyDistributedThunk()) {
1138+
if (auto distributedThunk = afd->getDistributedThunk())
1139+
return SILDeclRef(distributedThunk).asDistributed();
1140+
}
1141+
1142+
// A call to `@_backDeploy` function may need to go through a thunk.
1143+
if (shouldApplyBackDeploymentThunk(afd, ctx,
1144+
SGF.F.getResilienceExpansion())) {
1145+
return SILDeclRef(afd).asBackDeploymentKind(
1146+
SILDeclRef::BackDeploymentKind::Thunk);
1147+
}
1148+
1149+
return SILDeclRef(afd).asForeign(
1150+
!isConstructorWithGeneratedAllocatorThunk(afd) &&
1151+
requiresForeignEntryPoint(afd));
1152+
}
1153+
11071154
//
11081155
// Known callees.
11091156
//
@@ -1144,25 +1191,7 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11441191
}
11451192

11461193
// Otherwise, we have a statically-dispatched call.
1147-
SILDeclRef constant = SILDeclRef(e->getDecl());
1148-
1149-
/// Some special handling may be necessary for thunks:
1150-
if (callSite && callSite->shouldApplyDistributedThunk()) {
1151-
if (auto distributedThunk = cast<AbstractFunctionDecl>(e->getDecl())->getDistributedThunk()) {
1152-
constant = SILDeclRef(distributedThunk).asDistributed();
1153-
}
1154-
} else if (afd->isBackDeployed()) {
1155-
// If we're calling a back deployed function then we need to call a
1156-
// thunk instead that will handle the fallback when the original
1157-
// function is unavailable at runtime.
1158-
constant =
1159-
constant.asBackDeploymentKind(SILDeclRef::BackDeploymentKind::Thunk);
1160-
} else {
1161-
constant = constant.asForeign(
1162-
!isConstructorWithGeneratedAllocatorThunk(e->getDecl())
1163-
&& requiresForeignEntryPoint(e->getDecl()));
1164-
}
1165-
1194+
SILDeclRef constant = getDeclRefForStaticDispatchApply(e);
11661195
auto captureInfo = SGF.SGM.Types.getLoweredLocalCaptures(constant);
11671196
SGF.SGM.Types.setCaptureTypeExpansionContext(constant, SGF.SGM.M);
11681197

@@ -5886,9 +5915,11 @@ SILGenFunction::prepareSubscriptIndices(SubscriptDecl *subscript,
58865915
return result;
58875916
}
58885917

5889-
SILDeclRef SILGenModule::getAccessorDeclRef(AccessorDecl *accessor) {
5918+
SILDeclRef SILGenModule::getAccessorDeclRef(AccessorDecl *accessor,
5919+
ResilienceExpansion expansion) {
58905920
auto declRef = SILDeclRef(accessor, SILDeclRef::Kind::Func);
5891-
if (accessor->isBackDeployed())
5921+
5922+
if (shouldApplyBackDeploymentThunk(accessor, getASTContext(), expansion))
58925923
return declRef.asBackDeploymentKind(SILDeclRef::BackDeploymentKind::Thunk);
58935924

58945925
return declRef.asForeign(requiresForeignEntryPoint(accessor));

lib/SILGen/SILGenExpr.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3548,6 +3548,7 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM,
35483548
static KeyPathPatternComponent::ComputedPropertyId
35493549
getIdForKeyPathComponentComputedProperty(SILGenModule &SGM,
35503550
AbstractStorageDecl *storage,
3551+
ResilienceExpansion expansion,
35513552
AccessStrategy strategy) {
35523553
switch (strategy.getKind()) {
35533554
case AccessStrategy::Storage:
@@ -3559,7 +3560,8 @@ getIdForKeyPathComponentComputedProperty(SILGenModule &SGM,
35593560
strategy = strategy.getReadStrategy();
35603561
if (strategy.getKind() != AccessStrategy::Storage ||
35613562
!getRepresentativeAccessorForKeyPath(storage)) {
3562-
return getIdForKeyPathComponentComputedProperty(SGM, storage, strategy);
3563+
return getIdForKeyPathComponentComputedProperty(SGM, storage, expansion,
3564+
strategy);
35633565
}
35643566
LLVM_FALLTHROUGH;
35653567
case AccessStrategy::DirectToAccessor: {
@@ -3579,7 +3581,8 @@ getIdForKeyPathComponentComputedProperty(SILGenModule &SGM,
35793581
}
35803582
case AccessStrategy::DispatchToAccessor: {
35813583
// Identify the property by its vtable or wtable slot.
3582-
return SGM.getAccessorDeclRef(getRepresentativeAccessorForKeyPath(storage));
3584+
return SGM.getAccessorDeclRef(getRepresentativeAccessorForKeyPath(storage),
3585+
expansion);
35833586
}
35843587

35853588
case AccessStrategy::DispatchToDistributedThunk: {
@@ -3702,7 +3705,7 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
37023705
// either.
37033706
if (baseDecl->requiresOpaqueAccessors()) {
37043707
auto representative = getAccessorDeclRef(
3705-
getRepresentativeAccessorForKeyPath(baseDecl));
3708+
getRepresentativeAccessorForKeyPath(baseDecl), expansion);
37063709
if (representative.isForeign)
37073710
return false;
37083711
if (representative.getLinkage(ForDefinition) > SILLinkage::PublicNonABI)
@@ -3781,7 +3784,7 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
37813784

37823785
// We need thunks to bring the getter and setter to the right signature
37833786
// expected by the key path runtime.
3784-
auto id = getIdForKeyPathComponentComputedProperty(*this, var,
3787+
auto id = getIdForKeyPathComponentComputedProperty(*this, var, expansion,
37853788
strategy);
37863789
auto getter = getOrCreateKeyPathGetter(*this,
37873790
var, subs,
@@ -3838,8 +3841,9 @@ SILGenModule::emitKeyPathComponentForDecl(SILLocation loc,
38383841
indexPatterns,
38393842
indexEquals, indexHash);
38403843
}
3841-
3842-
auto id = getIdForKeyPathComponentComputedProperty(*this, decl, strategy);
3844+
3845+
auto id = getIdForKeyPathComponentComputedProperty(*this, decl, expansion,
3846+
strategy);
38433847
auto getter = getOrCreateKeyPathGetter(*this,
38443848
decl, subs,
38453849
needsGenericContext ? genericEnv : nullptr,

lib/SILGen/SILGenFunction.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2370,6 +2370,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
23702370

23712371
/// Get the _Pointer protocol used for pointer argument operations.
23722372
ProtocolDecl *getPointerProtocol();
2373+
2374+
/// Returns the SILDeclRef to use for references to the given accessor.
2375+
SILDeclRef getAccessorDeclRef(AccessorDecl *accessor) {
2376+
return SGM.getAccessorDeclRef(accessor, F.getResilienceExpansion());
2377+
}
23732378
};
23742379

23752380

lib/SILGen/SILGenLValue.cpp

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2887,7 +2887,7 @@ namespace {
28872887
void emitUsingAccessor(AccessorKind accessorKind,
28882888
bool isDirect) {
28892889
auto accessor =
2890-
SGF.SGM.getAccessorDeclRef(Storage->getOpaqueAccessor(accessorKind));
2890+
SGF.getAccessorDeclRef(Storage->getOpaqueAccessor(accessorKind));
28912891

28922892
switch (accessorKind) {
28932893
case AccessorKind::Set: {
@@ -3318,16 +3318,18 @@ LValue SILGenLValue::visitDotSyntaxBaseIgnoredExpr(DotSyntaxBaseIgnoredExpr *e,
33183318
static SGFAccessKind getBaseAccessKindForAccessor(SILGenModule &SGM,
33193319
AccessorDecl *accessor,
33203320
CanType baseFormalType) {
3321-
if (accessor->isMutating()) {
3321+
if (accessor->isMutating())
33223322
return SGFAccessKind::ReadWrite;
3323-
} else if (SGM.shouldEmitSelfAsRValue(accessor, baseFormalType)) {
3324-
return SGM.isNonMutatingSelfIndirect(SGM.getAccessorDeclRef(accessor))
3325-
? SGFAccessKind::OwnedAddressRead
3326-
: SGFAccessKind::OwnedObjectRead;
3323+
3324+
auto declRef = SGM.getAccessorDeclRef(accessor, ResilienceExpansion::Minimal);
3325+
if (SGM.shouldEmitSelfAsRValue(accessor, baseFormalType)) {
3326+
return SGM.isNonMutatingSelfIndirect(declRef)
3327+
? SGFAccessKind::OwnedAddressRead
3328+
: SGFAccessKind::OwnedObjectRead;
33273329
} else {
3328-
return SGM.isNonMutatingSelfIndirect(SGM.getAccessorDeclRef(accessor))
3329-
? SGFAccessKind::BorrowedAddressRead
3330-
: SGFAccessKind::BorrowedObjectRead;
3330+
return SGM.isNonMutatingSelfIndirect(declRef)
3331+
? SGFAccessKind::BorrowedAddressRead
3332+
: SGFAccessKind::BorrowedObjectRead;
33313333
}
33323334
}
33333335

@@ -3435,7 +3437,7 @@ LValue SILGenLValue::visitMemberRefExpr(MemberRefExpr *e,
34353437
var->getOpaqueAccessor(AccessorKind::Read)) {
34363438
bool isObjC = false;
34373439
auto readAccessor =
3438-
SGF.SGM.getAccessorDeclRef(var->getOpaqueAccessor(AccessorKind::Read));
3440+
SGF.getAccessorDeclRef(var->getOpaqueAccessor(AccessorKind::Read));
34393441
if (isCallToReplacedInDynamicReplacement(
34403442
SGF, readAccessor.getAbstractFunctionDecl(), isObjC)) {
34413443
accessSemantics = AccessSemantics::DirectToImplementation;
@@ -3636,7 +3638,7 @@ LValue SILGenLValue::visitSubscriptExpr(SubscriptExpr *e,
36363638
decl->getOpaqueAccessor(AccessorKind::Read)) {
36373639
bool isObjC = false;
36383640
auto readAccessor =
3639-
SGF.SGM.getAccessorDeclRef(decl->getOpaqueAccessor(AccessorKind::Read));
3641+
SGF.getAccessorDeclRef(decl->getOpaqueAccessor(AccessorKind::Read));
36403642
if (isCallToReplacedInDynamicReplacement(
36413643
SGF, readAccessor.getAbstractFunctionDecl(), isObjC)) {
36423644
accessSemantics = AccessSemantics::DirectToImplementation;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: %target-swift-emit-sil -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 -verify
2+
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s | %FileCheck %s
3+
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s --check-prefixes=CHECK,CHECK-BACK-DEPLOY
4+
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.52 | %FileCheck %s --check-prefixes=CHECK,CHECK-NATIVE
5+
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.60 | %FileCheck %s --check-prefixes=CHECK,CHECK-NATIVE
6+
7+
// REQUIRES: OS=macosx
8+
9+
@_backDeploy(before: macOS 10.52)
10+
public func someFunc() {}
11+
12+
public struct S<T> {
13+
@usableFromInline var _x: T
14+
15+
@_backDeploy(before: macOS 10.52)
16+
public var x: T {
17+
get { _x }
18+
set { _x = newValue }
19+
}
20+
}
21+
22+
public struct Z {
23+
public init() {}
24+
}
25+
26+
// CHECK-LABEL: sil hidden [ossa] @$s11back_deploy15resilientCalleryyAA1SVyAA1ZVGzF
27+
func resilientCaller(_ s: inout S<Z>) {
28+
// CHECK-BACK-DEPLOY: function_ref @$s11back_deploy8someFuncyyFTwb : $@convention(thin) () -> ()
29+
// CHECK-NATIVE: function_ref @$s11back_deploy8someFuncyyF : $@convention(thin) () -> ()
30+
someFunc()
31+
// CHECK-BACK-DEPLOY: function_ref @$s11back_deploy1SV1xxvgTwb : $@convention(method) <τ_0_0> (@in_guaranteed S<τ_0_0>) -> @out τ_0_0
32+
// CHECK-NATIVE: function_ref @$s11back_deploy1SV1xxvg : $@convention(method) <τ_0_0> (@in_guaranteed S<τ_0_0>) -> @out τ_0_0
33+
_ = s.x
34+
// CHECK-BACK-DEPLOY: function_ref @$s11back_deploy1SV1xxvsTwb : $@convention(method) <τ_0_0> (@in τ_0_0, @inout S<τ_0_0>) -> ()
35+
// CHECK-NATIVE: function_ref @$s11back_deploy1SV1xxvs : $@convention(method) <τ_0_0> (@in τ_0_0, @inout S<τ_0_0>) -> ()
36+
s.x = Z()
37+
}
38+
39+
// CHECK-LABEL: sil [serialized] [ossa] @$s11back_deploy15inlinableCalleryyAA1SVyAA1ZVGzF
40+
@inlinable
41+
func inlinableCaller(_ s: inout S<Z>) {
42+
// CHECK: function_ref @$s11back_deploy8someFuncyyFTwb : $@convention(thin) () -> ()
43+
someFunc()
44+
// CHECK: function_ref @$s11back_deploy1SV1xxvgTwb : $@convention(method) <τ_0_0> (@in_guaranteed S<τ_0_0>) -> @out τ_0_0
45+
_ = s.x
46+
// CHECK: function_ref @$s11back_deploy1SV1xxvsTwb : $@convention(method) <τ_0_0> (@in τ_0_0, @inout S<τ_0_0>) -> ()
47+
s.x = Z()
48+
}
49+
50+
// CHECK-LABEL: sil non_abi [serialized] [ossa] @$s11back_deploy10aeicCalleryyAA1SVyAA1ZVGzF
51+
@_alwaysEmitIntoClient
52+
func aeicCaller(_ s: inout S<Z>) {
53+
// CHECK: function_ref @$s11back_deploy8someFuncyyFTwb : $@convention(thin) () -> ()
54+
someFunc()
55+
// CHECK: function_ref @$s11back_deploy1SV1xxvgTwb : $@convention(method) <τ_0_0> (@in_guaranteed S<τ_0_0>) -> @out τ_0_0
56+
_ = s.x
57+
// CHECK: function_ref @$s11back_deploy1SV1xxvsTwb : $@convention(method) <τ_0_0> (@in τ_0_0, @inout S<τ_0_0>) -> ()
58+
s.x = Z()
59+
}
60+
61+
// CHECK-LABEL: sil non_abi [serialized] [ossa] @$s11back_deploy0A14DeployedCalleryyAA1SVyAA1ZVGzFTwB
62+
@_backDeploy(before: macOS 10.52)
63+
public func backDeployedCaller(_ s: inout S<Z>) {
64+
// CHECK: function_ref @$s11back_deploy8someFuncyyFTwb : $@convention(thin) () -> ()
65+
someFunc()
66+
// CHECK: function_ref @$s11back_deploy1SV1xxvgTwb : $@convention(method) <τ_0_0> (@in_guaranteed S<τ_0_0>) -> @out τ_0_0
67+
_ = s.x
68+
// CHECK: function_ref @$s11back_deploy1SV1xxvsTwb : $@convention(method) <τ_0_0> (@in τ_0_0, @inout S<τ_0_0>) -> ()
69+
s.x = Z()
70+
}

test/SILGen/back_deploy_attribute_accessor.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// RUN: %target-swift-emit-sil -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 -verify
22
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s | %FileCheck %s
33
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s
4-
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.60 | %FileCheck %s
54

65
// REQUIRES: OS=macosx
76

test/SILGen/back_deploy_attribute_accessor_coroutine.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// RUN: %target-swift-emit-sil -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 -verify
22
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s | %FileCheck %s
33
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s
4-
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.60 | %FileCheck %s
54

65
// REQUIRES: OS=macosx
76

test/SILGen/back_deploy_attribute_async_func.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// RUN: %target-swift-emit-sil -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 -verify
22
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s | %FileCheck %s
33
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s
4-
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.60 | %FileCheck %s
54

65
// REQUIRES: OS=macosx
76
// REQUIRES: concurrency

test/SILGen/back_deploy_attribute_func.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// RUN: %target-swift-emit-sil -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 -verify
22
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s | %FileCheck %s
33
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.50 | %FileCheck %s
4-
// RUN: %target-swift-emit-silgen -parse-as-library -module-name back_deploy %s -target %target-cpu-apple-macosx10.60 | %FileCheck %s
54

65
// REQUIRES: OS=macosx
76

0 commit comments

Comments
 (0)