Skip to content

Commit 97e8e31

Browse files
committed
SILGen: Emit a thunk that wraps the call to a back deployed function. The thunk calls the original function if it is available at runtime and otherwise falls back to calling a copy that is emitted into the client (not yet implemented).
1 parent ff5abc4 commit 97e8e31

File tree

9 files changed

+316
-15
lines changed

9 files changed

+316
-15
lines changed

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,11 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {
317317
return maybeAddExternal(SILLinkage::PublicNonABI);
318318
}
319319

320+
// Back deployment thunks are emitted into the client and therefore have
321+
// PublicNonABI linkage.
322+
if (isBackDeployedThunk())
323+
return maybeAddExternal(SILLinkage::PublicNonABI);
324+
320325
enum class Limit {
321326
/// No limit.
322327
None,

lib/SILGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_swift_host_library(swiftSILGen STATIC
1111
SwitchEnumBuilder.cpp
1212
SILGen.cpp
1313
SILGenApply.cpp
14+
SILGenBackDeploy.cpp
1415
SILGenBridging.cpp
1516
SILGenBuilder.cpp
1617
SILGenBuiltin.cpp

lib/SILGen/SILGen.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,23 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
853853
return;
854854
}
855855

856+
if (constant.isBackDeployedThunk()) {
857+
auto loc = constant.getAsRegularLocation();
858+
loc.markAutoGenerated();
859+
auto *dc = loc.getAsDeclContext();
860+
assert(dc);
861+
862+
preEmitFunction(constant, f, loc);
863+
PrettyStackTraceSILFunction X("silgen emitBackDeployedThunk", f);
864+
f->setBare(IsBare);
865+
f->setThunk(IsThunk);
866+
867+
SILGenFunction(*this, *f, dc).emitBackDeployedThunk(constant);
868+
869+
postEmitFunction(constant, f);
870+
return;
871+
}
872+
856873
switch (constant.kind) {
857874
case SILDeclRef::Kind::Func: {
858875
if (auto *ce = constant.getAbstractClosureExpr()) {
@@ -1383,6 +1400,12 @@ void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) {
13831400
auto thunk = SILDeclRef(AFD).asDistributed();
13841401
emitDistributedThunk(thunk);
13851402
}
1403+
1404+
if (AFD->isBackDeployed()) {
1405+
// FIXME(backDeploy): Emit client copy of back deployed function
1406+
auto thunk = SILDeclRef(AFD).asBackDeployed();
1407+
emitBackDeployedThunk(thunk);
1408+
}
13861409
}
13871410

13881411
void SILGenModule::emitFunction(FuncDecl *fd) {

lib/SILGen/SILGen.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
335335

336336
/// Emits a thunk from an actor function to a potentially distributed call.
337337
void emitDistributedThunk(SILDeclRef thunk);
338-
338+
339+
/// Emits a thunk that calls either the original function if it is available
340+
/// or otherwise calls a copy of the function that was emitted into the
341+
/// calling module.
342+
void emitBackDeployedThunk(SILDeclRef thunk);
343+
339344
void preEmitFunction(SILDeclRef constant, SILFunction *F, SILLocation L);
340345
void postEmitFunction(SILDeclRef constant, SILFunction *F);
341346

lib/SILGen/SILGenApply.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,11 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
10301030
}
10311031

10321032
void processClassMethod(DeclRefExpr *e, AbstractFunctionDecl *afd) {
1033+
// FIXME(backDeploy): Investigate support for back deployed class method
1034+
// calls
1035+
assert(!afd->isBackDeployed() &&
1036+
"back deployed method calls are unsupported");
1037+
10331038
ArgumentSource selfArgSource(selfApply->getBase());
10341039
setSelfParam(std::move(selfArgSource));
10351040

@@ -1119,6 +1124,10 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11191124
auto constant = SILDeclRef(e->getDecl());
11201125
if (callSite && callSite->shouldApplyDistributedThunk()) {
11211126
constant = constant.asDistributed(true);
1127+
} else if (e->getDecl()->getAttrs().hasAttribute<BackDeployAttr>()) {
1128+
// If we're calling a back deployed function we need to call through
1129+
// a thunk instead that handles availability.
1130+
constant = constant.asBackDeployed(true);
11221131
} else {
11231132
constant = constant.asForeign(
11241133
!isConstructorWithGeneratedAllocatorThunk(e->getDecl())
@@ -5137,6 +5146,8 @@ RValue SILGenFunction::emitApplyMethod(SILLocation loc, ConcreteDeclRef declRef,
51375146

51385147
if (call->isDistributed()) {
51395148
callRef = callRef.asDistributed(true);
5149+
} else if (call->isBackDeployed()) {
5150+
callRef = callRef.asBackDeployed(true);
51405151
}
51415152

51425153
auto declRefConstant = getConstantInfo(getTypeExpansionContext(), callRef);

lib/SILGen/SILGenBackDeploy.cpp

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
//===--- SILGenBackDeploy.cpp - SILGen for back deployment ----------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "SILGenFunction.h"
14+
#include "SILGenFunctionBuilder.h"
15+
#include "Scope.h"
16+
#include "swift/SIL/SILDeclRef.h"
17+
18+
using namespace swift;
19+
using namespace Lowering;
20+
21+
/// Emit the following branch SIL instruction:
22+
/// \verbatim
23+
/// if #available(OSVersion) {
24+
/// <availableBB>
25+
/// } else {
26+
/// <unavailableBB>
27+
/// }
28+
/// \endverbatim
29+
static void emitBackDeployIfAvailableCondition(SILGenFunction &SGF,
30+
AbstractFunctionDecl *AFD,
31+
SILLocation loc,
32+
SILBasicBlock *availableBB,
33+
SILBasicBlock *unavailableBB) {
34+
PlatformKind platform = targetPlatform(SGF.SGM.getASTContext().LangOpts);
35+
auto introduced = AFD->getIntroducedOSVersion(platform);
36+
VersionRange OSVersion = VersionRange::empty();
37+
if (introduced.hasValue()) {
38+
OSVersion = VersionRange::allGTE(*introduced);
39+
}
40+
41+
SILValue booleanTestValue;
42+
if (OSVersion.isEmpty() || OSVersion.isAll()) {
43+
// If there's no check for the current platform, this condition is
44+
// trivially true.
45+
SILType i1 = SILType::getBuiltinIntegerType(1, SGF.getASTContext());
46+
booleanTestValue = SGF.B.createIntegerLiteral(loc, i1, 1);
47+
} else {
48+
booleanTestValue = SGF.emitOSVersionRangeCheck(loc, OSVersion);
49+
}
50+
51+
SGF.B.createCondBranch(loc, booleanTestValue, availableBB, unavailableBB);
52+
}
53+
54+
/// Emits a function or method application, forwarding parameters.
55+
SILValue emitBackDeployForwardApply(SILGenFunction &SGF,
56+
AbstractFunctionDecl *AFD, SILLocation loc,
57+
SILDeclRef function,
58+
SmallVector<SILValue, 8> &params,
59+
SILBasicBlock *rethrowBB) {
60+
TypeExpansionContext TEC = SGF.getTypeExpansionContext();
61+
auto fnType = SGF.SGM.Types.getConstantOverrideType(TEC, function);
62+
auto silFnType =
63+
SILType::getPrimitiveObjectType(fnType).castTo<SILFunctionType>();
64+
SILFunctionConventions fnConv(silFnType, SGF.SGM.M);
65+
66+
if (silFnType->hasErrorResult())
67+
assert(rethrowBB && "must provide rethrow basic block");
68+
69+
bool isClassMethod = false;
70+
if (auto classDecl = dyn_cast<ClassDecl>(AFD->getDeclContext())) {
71+
if (!classDecl->isFinal() && !AFD->isFinal() &&
72+
!AFD->hasForcedStaticDispatch())
73+
isClassMethod = true;
74+
}
75+
76+
SILBasicBlock *normalBB = SGF.createBasicBlock();
77+
SILBasicBlock *errorBB = rethrowBB ? SGF.createBasicBlock() : nullptr;
78+
79+
SILValue functionRef;
80+
if (isClassMethod) {
81+
auto selfValue = ManagedValue::forUnmanaged(SGF.F.getSelfArgument());
82+
functionRef =
83+
SGF.emitClassMethodRef(loc, selfValue.getValue(), function, fnType);
84+
} else {
85+
functionRef = SGF.emitGlobalFunctionRef(loc, function);
86+
}
87+
auto subs = SGF.F.getForwardingSubstitutionMap();
88+
89+
if (errorBB) {
90+
SGF.B.createTryApply(loc, functionRef, subs, params, normalBB, errorBB);
91+
} else {
92+
auto result = SGF.B.createApply(loc, functionRef, subs, params);
93+
SGF.B.createBranch(loc, normalBB, {result});
94+
}
95+
96+
if (errorBB) {
97+
SGF.B.emitBlock(errorBB);
98+
SILValue error = errorBB->createPhiArgument(fnConv.getSILErrorType(TEC),
99+
OwnershipKind::Owned);
100+
SGF.B.createBranch(loc, rethrowBB, {error});
101+
}
102+
103+
SGF.B.emitBlock(normalBB);
104+
return normalBB->createPhiArgument(fnConv.getSILResultType(TEC),
105+
OwnershipKind::Owned);
106+
}
107+
108+
void SILGenFunction::emitBackDeployedThunk(SILDeclRef thunk) {
109+
// Generate code equivalent to:
110+
//
111+
// func X_thunk(...) async throws -> ... {
112+
// if #available(...) {
113+
// return try await X(...)
114+
// } else {
115+
// return try await X_clientCopy(...)
116+
// }
117+
// }
118+
119+
assert(thunk.isBackDeployed);
120+
121+
// Get a reference to the original declaration. We'll call the original
122+
// declaration at runtime if it is available.
123+
SILDeclRef original = thunk.asBackDeployed(false);
124+
auto AFD = cast<AbstractFunctionDecl>(thunk.getDecl());
125+
126+
// Use the same generic environment as the original entry point.
127+
F.setGenericEnvironment(SGM.Types.getConstantGenericEnvironment(original));
128+
129+
auto loc = thunk.getAsRegularLocation();
130+
loc.markAutoGenerated();
131+
Scope scope(Cleanups, CleanupLocation(loc));
132+
133+
auto methodTy =
134+
SGM.Types.getConstantOverrideType(getTypeExpansionContext(), thunk);
135+
auto derivativeFnSILTy = SILType::getPrimitiveObjectType(methodTy);
136+
auto silFnType = derivativeFnSILTy.castTo<SILFunctionType>();
137+
SILFunctionConventions fnConv(silFnType, SGM.M);
138+
auto resultType = fnConv.getSILResultType(getTypeExpansionContext());
139+
140+
// Gather params for forwarding.
141+
SmallVector<SILValue, 8> paramsForForwarding;
142+
bindParametersForForwarding(AFD->getParameters(), paramsForForwarding);
143+
if (auto *selfDecl = AFD->getImplicitSelfDecl())
144+
bindParameterForForwarding(selfDecl, paramsForForwarding);
145+
146+
auto rethrowBB =
147+
silFnType->hasErrorResult() ? createBasicBlock("rethrowBB") : nullptr;
148+
auto returnBB = createBasicBlock("returnBB");
149+
150+
SILBasicBlock *availableBB = createBasicBlock("availableBB");
151+
SILBasicBlock *unavailableBB = createBasicBlock("unavailableBB");
152+
153+
// if #available(...) {
154+
// <availableBB>
155+
// } else {
156+
// <unavailableBB>
157+
// }
158+
emitBackDeployIfAvailableCondition(*this, AFD, loc, availableBB,
159+
unavailableBB);
160+
161+
// <availableBB>:
162+
// return (try)? (await)? (self.)?X(...)
163+
{
164+
B.emitBlock(availableBB);
165+
SILValue result = emitBackDeployForwardApply(
166+
*this, AFD, loc, original, paramsForForwarding, rethrowBB);
167+
B.createBranch(loc, returnBB, {result});
168+
}
169+
170+
// <unavailableBB>:
171+
// return (try)? (await)? (self.)?X_clientCopy(...)
172+
{
173+
B.emitBlock(unavailableBB);
174+
// FIXME(backDeploy): Emit a forward apply of client copy of function
175+
ManagedValue undef = emitUndef(resultType);
176+
B.createBranch(loc, returnBB, {undef});
177+
}
178+
179+
// Emit return logic.
180+
{
181+
B.emitBlock(returnBB);
182+
SILValue result =
183+
returnBB->createPhiArgument(resultType, OwnershipKind::Owned);
184+
185+
B.createReturn(loc, result);
186+
}
187+
188+
// Emit the rethrow logic.
189+
if (rethrowBB) {
190+
B.emitBlock(rethrowBB);
191+
SILValue error = rethrowBB->createPhiArgument(
192+
fnConv.getSILErrorType(getTypeExpansionContext()),
193+
OwnershipKind::Owned);
194+
195+
Cleanups.emitCleanupsForReturn(CleanupLocation(loc), IsForUnwind);
196+
B.createThrow(loc, error);
197+
}
198+
}

lib/SILGen/SILGenFunction.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2034,6 +2034,15 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
20342034
CanSILFunctionType toType,
20352035
bool reorderSelf);
20362036

2037+
//===--------------------------------------------------------------------===//
2038+
// Back Deployment thunks
2039+
//===--------------------------------------------------------------------===//
2040+
2041+
/// Invokes the original, back deployed declaration if it is available at
2042+
/// runtime. Otherwise, invoke a copy of the original function emitted
2043+
/// locally.
2044+
void emitBackDeployedThunk(SILDeclRef thunk);
2045+
20372046
//===---------------------------------------------------------------------===//
20382047
// Distributed Actors
20392048
//===---------------------------------------------------------------------===//

lib/SILGen/SILGenThunk.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ void SILGenModule::emitDistributedThunk(SILDeclRef thunk) {
112112
emitFunctionDefinition(thunk, getFunction(thunk, ForDefinition));
113113
}
114114

115+
void SILGenModule::emitBackDeployedThunk(SILDeclRef thunk) {
116+
// Thunks are always emitted by need, so don't need delayed emission.
117+
assert(thunk.isBackDeployedThunk() && "back deployed thunks only");
118+
emitFunctionDefinition(thunk, getFunction(thunk, ForDefinition));
119+
}
120+
115121
SILValue
116122
SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant,
117123
SILConstantInfo constantInfo,

0 commit comments

Comments
 (0)