Skip to content

Commit 5339dae

Browse files
committed
SILGen: Emit a fallback variant of a back deployable function and call it when the original function is not available at run time.
1 parent 123f953 commit 5339dae

15 files changed

+451
-239
lines changed

include/swift/AST/Decl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6268,7 +6268,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
62686268
/// Returns 'true' if the function is distributed.
62696269
bool isDistributed() const;
62706270

6271-
/// Returns 'true' if the function can be back deployed.
6271+
/// Returns 'true' if the function has the @c @_backDeploy attribute.
62726272
bool isBackDeployed() const;
62736273

62746274
PolymorphicEffectKind getPolymorphicEffectKind(EffectKind kind) const;

include/swift/SIL/SILDeclRef.h

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,21 @@ struct SILDeclRef {
162162
AsyncEntryPoint,
163163
};
164164

165+
/// Represents the variants of a back deployable function.
166+
enum class BackDeploymentKind : unsigned {
167+
/// Default value. If a SILDecRef references a function that has been back
168+
/// deployed and has this back deployment kind, then it references the
169+
/// original ABI stable function.
170+
None,
171+
/// The thunk variant of a function that calls either the original function
172+
/// or the fallback variant if the original is unavailable. This thunk will
173+
/// be emitted with PublicNonABI linkage.
174+
Thunk,
175+
/// The fallback variant of the function. This function will be emitted with
176+
/// PublicNonABI linkage.
177+
Fallback,
178+
};
179+
165180
/// The AST node represented by this SILDeclRef.
166181
Loc loc;
167182
/// The Kind of this SILDeclRef.
@@ -170,8 +185,8 @@ struct SILDeclRef {
170185
unsigned isForeign : 1;
171186
/// True if this references a distributed function.
172187
unsigned isDistributed : 1;
173-
/// True if this references a back deployed entry point for the referenced decl.
174-
unsigned isBackDeployed : 1;
188+
/// The BackDeploymentKind of this SILDeclRef.
189+
BackDeploymentKind backDeploymentKind : 2;
175190
/// The default argument index for a default argument getter.
176191
unsigned defaultArgIndex : 10;
177192

@@ -207,14 +222,14 @@ struct SILDeclRef {
207222
/// Produces a null SILDeclRef.
208223
SILDeclRef()
209224
: loc(), kind(Kind::Func), isForeign(0), isDistributed(0),
210-
isBackDeployed(0), defaultArgIndex(0) {}
225+
backDeploymentKind(BackDeploymentKind::None), defaultArgIndex(0) {}
211226

212227
/// Produces a SILDeclRef of the given kind for the given decl.
213228
explicit SILDeclRef(
214229
ValueDecl *decl, Kind kind,
215230
bool isForeign = false,
216231
bool isDistributed = false,
217-
bool isBackDeployed = false,
232+
BackDeploymentKind backDeploymentKind = BackDeploymentKind::None,
218233
AutoDiffDerivativeFunctionIdentifier *derivativeId = nullptr);
219234

220235
/// Produces a SILDeclRef for the given ValueDecl or
@@ -231,8 +246,7 @@ struct SILDeclRef {
231246
explicit SILDeclRef(
232247
Loc loc,
233248
bool isForeign = false,
234-
bool isDistributed = false,
235-
bool isBackDeployed = false);
249+
bool isDistributed = false);
236250

237251
/// See above put produces a prespecialization according to the signature.
238252
explicit SILDeclRef(Loc loc, GenericSignature prespecializationSig);
@@ -368,7 +382,7 @@ struct SILDeclRef {
368382
return loc.getOpaqueValue() == rhs.loc.getOpaqueValue() &&
369383
kind == rhs.kind && isForeign == rhs.isForeign &&
370384
isDistributed == rhs.isDistributed &&
371-
isBackDeployed == rhs.isBackDeployed &&
385+
backDeploymentKind == rhs.backDeploymentKind &&
372386
defaultArgIndex == rhs.defaultArgIndex &&
373387
pointer == rhs.pointer;
374388
}
@@ -387,7 +401,7 @@ struct SILDeclRef {
387401
return SILDeclRef(loc.getOpaqueValue(), kind,
388402
/*foreign=*/foreign,
389403
/*distributed=*/false,
390-
/*backDeployed=*/false,
404+
backDeploymentKind,
391405
defaultArgIndex,
392406
pointer.get<AutoDiffDerivativeFunctionIdentifier *>());
393407
}
@@ -397,17 +411,16 @@ struct SILDeclRef {
397411
return SILDeclRef(loc.getOpaqueValue(), kind,
398412
/*foreign=*/false,
399413
/*distributed=*/distributed,
400-
/*backDeployed=*/false,
414+
backDeploymentKind,
401415
defaultArgIndex,
402416
pointer.get<AutoDiffDerivativeFunctionIdentifier *>());
403417
}
404-
/// Returns the back deployment entry point corresponding to the same
405-
/// decl.
406-
SILDeclRef asBackDeployed(bool backDeployed = true) const {
418+
/// Returns a copy of the decl with the given back deployment kind.
419+
SILDeclRef asBackDeploymentKind(BackDeploymentKind backDeploymentKind) const {
407420
return SILDeclRef(loc.getOpaqueValue(), kind,
408-
/*foreign=*/false,
409-
/*distributed=*/false,
410-
/*backDeployed=*/backDeployed,
421+
isForeign,
422+
isDistributed,
423+
backDeploymentKind,
411424
defaultArgIndex,
412425
pointer.get<AutoDiffDerivativeFunctionIdentifier *>());
413426
}
@@ -452,9 +465,13 @@ struct SILDeclRef {
452465
/// True if the decl ref references a thunk handling potentially distributed actor functions
453466
bool isDistributedThunk() const;
454467

455-
/// True if the decl ref references a thunk handling a call to a back deployed
456-
/// function.
457-
bool isBackDeployedThunk() const;
468+
/// True if the decl ref references a thunk handling a call to a function that
469+
/// supports back deployment.
470+
bool isBackDeploymentThunk() const;
471+
472+
/// True if the decl ref references a function that is the back deployment
473+
/// fallback for an original function which may be unavailable at runtime.
474+
bool isBackDeploymentFallback() const;
458475

459476
/// True if the decl ref references a method which introduces a new vtable
460477
/// entry.
@@ -533,12 +550,13 @@ struct SILDeclRef {
533550
explicit SILDeclRef(void *opaqueLoc, Kind kind,
534551
bool isForeign,
535552
bool isDistributed,
536-
bool isBackDeployed,
553+
BackDeploymentKind backDeploymentKind,
537554
unsigned defaultArgIndex,
538555
AutoDiffDerivativeFunctionIdentifier *derivativeId)
539556
: loc(Loc::getFromOpaqueValue(opaqueLoc)), kind(kind),
540557
isForeign(isForeign), isDistributed(isDistributed),
541-
isBackDeployed(isBackDeployed), defaultArgIndex(defaultArgIndex),
558+
backDeploymentKind(backDeploymentKind),
559+
defaultArgIndex(defaultArgIndex),
542560
pointer(derivativeId) {}
543561
};
544562

@@ -555,17 +573,18 @@ namespace llvm {
555573
template<> struct DenseMapInfo<swift::SILDeclRef> {
556574
using SILDeclRef = swift::SILDeclRef;
557575
using Kind = SILDeclRef::Kind;
576+
using BackDeploymentKind = SILDeclRef::BackDeploymentKind;
558577
using Loc = SILDeclRef::Loc;
559578
using PointerInfo = DenseMapInfo<void*>;
560579
using UnsignedInfo = DenseMapInfo<unsigned>;
561580

562581
static SILDeclRef getEmptyKey() {
563582
return SILDeclRef(PointerInfo::getEmptyKey(), Kind::Func, false, false,
564-
false, 0, nullptr);
583+
BackDeploymentKind::None, 0, nullptr);
565584
}
566585
static SILDeclRef getTombstoneKey() {
567586
return SILDeclRef(PointerInfo::getTombstoneKey(), Kind::Func, false, false,
568-
false, 0, nullptr);
587+
BackDeploymentKind::None, 0, nullptr);
569588
}
570589
static unsigned getHashValue(swift::SILDeclRef Val) {
571590
unsigned h1 = PointerInfo::getHashValue(Val.loc.getOpaqueValue());
@@ -576,7 +595,7 @@ template<> struct DenseMapInfo<swift::SILDeclRef> {
576595
unsigned h4 = UnsignedInfo::getHashValue(Val.isForeign);
577596
unsigned h5 = PointerInfo::getHashValue(Val.pointer.getOpaqueValue());
578597
unsigned h6 = UnsignedInfo::getHashValue(Val.isDistributed);
579-
unsigned h7 = UnsignedInfo::getHashValue(Val.isBackDeployed);
598+
unsigned h7 = UnsignedInfo::getHashValue(unsigned(Val.backDeploymentKind));
580599
return h1 ^ (h2 << 4) ^ (h3 << 9) ^ (h4 << 7) ^ (h5 << 11) ^ (h6 << 8) ^
581600
(h7 << 10);
582601
}

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -121,15 +121,18 @@ bool swift::requiresForeignEntryPoint(ValueDecl *vd) {
121121
return false;
122122
}
123123

124-
SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind,
125-
bool isForeign, bool isDistributed, bool isBackDeployed,
124+
SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, bool isForeign,
125+
bool isDistributed,
126+
SILDeclRef::BackDeploymentKind backDeploymentKind,
126127
AutoDiffDerivativeFunctionIdentifier *derivativeId)
127128
: loc(vd), kind(kind), isForeign(isForeign), isDistributed(isDistributed),
128-
isBackDeployed(isBackDeployed), defaultArgIndex(0), pointer(derivativeId) {}
129+
backDeploymentKind(backDeploymentKind), defaultArgIndex(0),
130+
pointer(derivativeId) {}
129131

130132
SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign,
131-
bool asDistributed, bool asBackDeployed)
132-
: defaultArgIndex(0),
133+
bool asDistributed)
134+
: backDeploymentKind(SILDeclRef::BackDeploymentKind::None),
135+
defaultArgIndex(0),
133136
pointer((AutoDiffDerivativeFunctionIdentifier *)nullptr) {
134137
if (auto *vd = baseLoc.dyn_cast<ValueDecl*>()) {
135138
if (auto *fd = dyn_cast<FuncDecl>(vd)) {
@@ -168,7 +171,6 @@ SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign,
168171

169172
isForeign = asForeign;
170173
isDistributed = asDistributed;
171-
isBackDeployed = asBackDeployed;
172174
}
173175

174176
SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc,
@@ -205,7 +207,7 @@ ASTContext &SILDeclRef::getASTContext() const {
205207

206208
bool SILDeclRef::isThunk() const {
207209
return isForeignToNativeThunk() || isNativeToForeignThunk() ||
208-
isDistributedThunk() || isBackDeployedThunk();
210+
isDistributedThunk() || isBackDeploymentThunk();
209211
}
210212

211213
bool SILDeclRef::isClangImported() const {
@@ -317,9 +319,9 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {
317319
return maybeAddExternal(SILLinkage::PublicNonABI);
318320
}
319321

320-
// Back deployment thunks are emitted into the client and therefore have
321-
// PublicNonABI linkage.
322-
if (isBackDeployedThunk())
322+
// Back deployment thunks and fallbacks are emitted into the client and
323+
// therefore have PublicNonABI linkage.
324+
if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None)
323325
return maybeAddExternal(SILLinkage::PublicNonABI);
324326

325327
enum class Limit {
@@ -736,10 +738,8 @@ bool SILDeclRef::isAlwaysInline() const {
736738
}
737739

738740
bool SILDeclRef::isAnyThunk() const {
739-
return isForeignToNativeThunk() ||
740-
isNativeToForeignThunk() ||
741-
isDistributedThunk() ||
742-
isBackDeployedThunk();
741+
return isForeignToNativeThunk() || isNativeToForeignThunk() ||
742+
isDistributedThunk() || isBackDeploymentThunk();
743743
}
744744

745745
bool SILDeclRef::isForeignToNativeThunk() const {
@@ -793,8 +793,14 @@ bool SILDeclRef::isDistributedThunk() const {
793793
return kind == Kind::Func;
794794
}
795795

796-
bool SILDeclRef::isBackDeployedThunk() const {
797-
if (!isBackDeployed)
796+
bool SILDeclRef::isBackDeploymentFallback() const {
797+
if (backDeploymentKind != BackDeploymentKind::Fallback)
798+
return false;
799+
return kind == Kind::Func;
800+
}
801+
802+
bool SILDeclRef::isBackDeploymentThunk() const {
803+
if (backDeploymentKind != BackDeploymentKind::Thunk)
798804
return false;
799805
return kind == Kind::Func;
800806
}
@@ -879,8 +885,10 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const {
879885
SKind = ASTMangler::SymbolKind::ObjCAsSwiftThunk;
880886
} else if (isDistributedThunk()) {
881887
SKind = ASTMangler::SymbolKind::DistributedThunk;
882-
} else if (isBackDeployedThunk()) {
883-
SKind = ASTMangler::SymbolKind::BackDeployedThunk;
888+
} else if (isBackDeploymentThunk()) {
889+
SKind = ASTMangler::SymbolKind::BackDeploymentThunk;
890+
} else if (isBackDeploymentFallback()) {
891+
SKind = ASTMangler::SymbolKind::BackDeploymentFallback;
884892
}
885893
break;
886894
case SILDeclRef::ManglingKind::DynamicThunk:
@@ -1012,7 +1020,7 @@ bool SILDeclRef::requiresNewVTableEntry() const {
10121020
return false;
10131021
if (isDistributedThunk())
10141022
return false;
1015-
if (isBackDeployedThunk())
1023+
if (isBackDeploymentThunk())
10161024
return false;
10171025
auto fnDecl = dyn_cast<AbstractFunctionDecl>(getDecl());
10181026
if (!fnDecl)
@@ -1041,6 +1049,9 @@ SILDeclRef SILDeclRef::getOverridden() const {
10411049

10421050
SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const {
10431051
if (auto overridden = getOverridden()) {
1052+
// Back deployed methods should not be overridden.
1053+
assert(backDeploymentKind == SILDeclRef::BackDeploymentKind::None);
1054+
10441055
// If we overrode a foreign decl or dynamic method, if this is an
10451056
// accessor for a property that overrides an ObjC decl, or if it is an
10461057
// @NSManaged property, then it won't be in the vtable.
@@ -1051,10 +1062,6 @@ SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const {
10511062
if (isDistributedThunk())
10521063
return SILDeclRef();
10531064

1054-
// Back deployed thunks are not in the vtable.
1055-
if (isBackDeployedThunk())
1056-
return SILDeclRef();
1057-
10581065
// An @objc convenience initializer can be "overridden" in the sense that
10591066
// its selector is reclaimed by a subclass's convenience init with the
10601067
// same name. The AST models this as an override for the purposes of
@@ -1336,7 +1343,7 @@ bool SILDeclRef::canBeDynamicReplacement() const {
13361343
return false;
13371344
if (isDistributedThunk())
13381345
return false;
1339-
if (isBackDeployedThunk())
1346+
if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None)
13401347
return false;
13411348
if (kind == SILDeclRef::Kind::Destroyer ||
13421349
kind == SILDeclRef::Kind::DefaultArgGenerator)
@@ -1357,7 +1364,7 @@ bool SILDeclRef::isDynamicallyReplaceable() const {
13571364
if (isDistributedThunk())
13581365
return false;
13591366

1360-
if (isBackDeployedThunk())
1367+
if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None)
13611368
return false;
13621369

13631370
if (kind == SILDeclRef::Kind::DefaultArgGenerator)

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,7 +1519,7 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result,
15191519
if (!P.consumeIf(tok::sil_exclamation)) {
15201520
// Construct SILDeclRef.
15211521
Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false,
1522-
/*backDeployed=*/false, DerivativeId);
1522+
SILDeclRef::BackDeploymentKind::None, DerivativeId);
15231523
return false;
15241524
}
15251525

@@ -1640,7 +1640,7 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result,
16401640

16411641
// Construct SILDeclRef.
16421642
Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false,
1643-
/*backDeployed=*/false, DerivativeId);
1643+
SILDeclRef::BackDeploymentKind::None, DerivativeId);
16441644
return false;
16451645
}
16461646

lib/SILGen/SILGen.cpp

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

856-
if (constant.isBackDeployedThunk()) {
856+
if (constant.isBackDeploymentThunk()) {
857857
auto loc = constant.getAsRegularLocation();
858858
loc.markAutoGenerated();
859859
auto *dc = loc.getAsDeclContext();
860860
assert(dc);
861861

862862
preEmitFunction(constant, f, loc);
863-
PrettyStackTraceSILFunction X("silgen emitBackDeployedThunk", f);
863+
PrettyStackTraceSILFunction X("silgen emitBackDeploymentThunk", f);
864864
f->setBare(IsBare);
865865
f->setThunk(IsThunk);
866866

867-
SILGenFunction(*this, *f, dc).emitBackDeployedThunk(constant);
867+
SILGenFunction(*this, *f, dc).emitBackDeploymentThunk(constant);
868868

869869
postEmitFunction(constant, f);
870870
return;
@@ -1402,9 +1402,17 @@ void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) {
14021402
}
14031403

14041404
if (AFD->isBackDeployed()) {
1405-
// FIXME(backDeploy): Emit client copy of back deployed function
1406-
auto thunk = SILDeclRef(AFD).asBackDeployed();
1407-
emitBackDeployedThunk(thunk);
1405+
// Emit the fallback function that will be used when the original function
1406+
// is unavailable at runtime.
1407+
auto fallback = SILDeclRef(AFD).asBackDeploymentKind(
1408+
SILDeclRef::BackDeploymentKind::Fallback);
1409+
emitFunctionDefinition(fallback, getFunction(fallback, ForDefinition));
1410+
1411+
// Emit the thunk that either invokes the original function or the fallback
1412+
// function depending on the availability of the original.
1413+
auto thunk = SILDeclRef(AFD).asBackDeploymentKind(
1414+
SILDeclRef::BackDeploymentKind::Thunk);
1415+
emitBackDeploymentThunk(thunk);
14081416
}
14091417
}
14101418

lib/SILGen/SILGen.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
337337
void emitDistributedThunk(SILDeclRef thunk);
338338

339339
/// 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);
340+
/// or otherwise calls a fallback variant of the function that was emitted
341+
/// into the client module.
342+
void emitBackDeploymentThunk(SILDeclRef thunk);
343343

344344
void preEmitFunction(SILDeclRef constant, SILFunction *F, SILLocation L);
345345
void postEmitFunction(SILDeclRef constant, SILFunction *F);

0 commit comments

Comments
 (0)