Skip to content

Commit

Permalink
SILGen: Emit a fallback variant of a back deployable function and cal…
Browse files Browse the repository at this point in the history
…l it when the original function is not available at run time.
  • Loading branch information
tshortli committed Feb 24, 2022
1 parent 123f953 commit 5339dae
Show file tree
Hide file tree
Showing 15 changed files with 451 additions and 239 deletions.
2 changes: 1 addition & 1 deletion include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6268,7 +6268,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
/// Returns 'true' if the function is distributed.
bool isDistributed() const;

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

PolymorphicEffectKind getPolymorphicEffectKind(EffectKind kind) const;
Expand Down
65 changes: 42 additions & 23 deletions include/swift/SIL/SILDeclRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,21 @@ struct SILDeclRef {
AsyncEntryPoint,
};

/// Represents the variants of a back deployable function.
enum class BackDeploymentKind : unsigned {
/// Default value. If a SILDecRef references a function that has been back
/// deployed and has this back deployment kind, then it references the
/// original ABI stable function.
None,
/// The thunk variant of a function that calls either the original function
/// or the fallback variant if the original is unavailable. This thunk will
/// be emitted with PublicNonABI linkage.
Thunk,
/// The fallback variant of the function. This function will be emitted with
/// PublicNonABI linkage.
Fallback,
};

/// The AST node represented by this SILDeclRef.
Loc loc;
/// The Kind of this SILDeclRef.
Expand All @@ -170,8 +185,8 @@ struct SILDeclRef {
unsigned isForeign : 1;
/// True if this references a distributed function.
unsigned isDistributed : 1;
/// True if this references a back deployed entry point for the referenced decl.
unsigned isBackDeployed : 1;
/// The BackDeploymentKind of this SILDeclRef.
BackDeploymentKind backDeploymentKind : 2;
/// The default argument index for a default argument getter.
unsigned defaultArgIndex : 10;

Expand Down Expand Up @@ -207,14 +222,14 @@ struct SILDeclRef {
/// Produces a null SILDeclRef.
SILDeclRef()
: loc(), kind(Kind::Func), isForeign(0), isDistributed(0),
isBackDeployed(0), defaultArgIndex(0) {}
backDeploymentKind(BackDeploymentKind::None), defaultArgIndex(0) {}

/// Produces a SILDeclRef of the given kind for the given decl.
explicit SILDeclRef(
ValueDecl *decl, Kind kind,
bool isForeign = false,
bool isDistributed = false,
bool isBackDeployed = false,
BackDeploymentKind backDeploymentKind = BackDeploymentKind::None,
AutoDiffDerivativeFunctionIdentifier *derivativeId = nullptr);

/// Produces a SILDeclRef for the given ValueDecl or
Expand All @@ -231,8 +246,7 @@ struct SILDeclRef {
explicit SILDeclRef(
Loc loc,
bool isForeign = false,
bool isDistributed = false,
bool isBackDeployed = false);
bool isDistributed = false);

/// See above put produces a prespecialization according to the signature.
explicit SILDeclRef(Loc loc, GenericSignature prespecializationSig);
Expand Down Expand Up @@ -368,7 +382,7 @@ struct SILDeclRef {
return loc.getOpaqueValue() == rhs.loc.getOpaqueValue() &&
kind == rhs.kind && isForeign == rhs.isForeign &&
isDistributed == rhs.isDistributed &&
isBackDeployed == rhs.isBackDeployed &&
backDeploymentKind == rhs.backDeploymentKind &&
defaultArgIndex == rhs.defaultArgIndex &&
pointer == rhs.pointer;
}
Expand All @@ -387,7 +401,7 @@ struct SILDeclRef {
return SILDeclRef(loc.getOpaqueValue(), kind,
/*foreign=*/foreign,
/*distributed=*/false,
/*backDeployed=*/false,
backDeploymentKind,
defaultArgIndex,
pointer.get<AutoDiffDerivativeFunctionIdentifier *>());
}
Expand All @@ -397,17 +411,16 @@ struct SILDeclRef {
return SILDeclRef(loc.getOpaqueValue(), kind,
/*foreign=*/false,
/*distributed=*/distributed,
/*backDeployed=*/false,
backDeploymentKind,
defaultArgIndex,
pointer.get<AutoDiffDerivativeFunctionIdentifier *>());
}
/// Returns the back deployment entry point corresponding to the same
/// decl.
SILDeclRef asBackDeployed(bool backDeployed = true) const {
/// Returns a copy of the decl with the given back deployment kind.
SILDeclRef asBackDeploymentKind(BackDeploymentKind backDeploymentKind) const {
return SILDeclRef(loc.getOpaqueValue(), kind,
/*foreign=*/false,
/*distributed=*/false,
/*backDeployed=*/backDeployed,
isForeign,
isDistributed,
backDeploymentKind,
defaultArgIndex,
pointer.get<AutoDiffDerivativeFunctionIdentifier *>());
}
Expand Down Expand Up @@ -452,9 +465,13 @@ struct SILDeclRef {
/// True if the decl ref references a thunk handling potentially distributed actor functions
bool isDistributedThunk() const;

/// True if the decl ref references a thunk handling a call to a back deployed
/// function.
bool isBackDeployedThunk() const;
/// True if the decl ref references a thunk handling a call to a function that
/// supports back deployment.
bool isBackDeploymentThunk() const;

/// True if the decl ref references a function that is the back deployment
/// fallback for an original function which may be unavailable at runtime.
bool isBackDeploymentFallback() const;

/// True if the decl ref references a method which introduces a new vtable
/// entry.
Expand Down Expand Up @@ -533,12 +550,13 @@ struct SILDeclRef {
explicit SILDeclRef(void *opaqueLoc, Kind kind,
bool isForeign,
bool isDistributed,
bool isBackDeployed,
BackDeploymentKind backDeploymentKind,
unsigned defaultArgIndex,
AutoDiffDerivativeFunctionIdentifier *derivativeId)
: loc(Loc::getFromOpaqueValue(opaqueLoc)), kind(kind),
isForeign(isForeign), isDistributed(isDistributed),
isBackDeployed(isBackDeployed), defaultArgIndex(defaultArgIndex),
backDeploymentKind(backDeploymentKind),
defaultArgIndex(defaultArgIndex),
pointer(derivativeId) {}
};

Expand All @@ -555,17 +573,18 @@ namespace llvm {
template<> struct DenseMapInfo<swift::SILDeclRef> {
using SILDeclRef = swift::SILDeclRef;
using Kind = SILDeclRef::Kind;
using BackDeploymentKind = SILDeclRef::BackDeploymentKind;
using Loc = SILDeclRef::Loc;
using PointerInfo = DenseMapInfo<void*>;
using UnsignedInfo = DenseMapInfo<unsigned>;

static SILDeclRef getEmptyKey() {
return SILDeclRef(PointerInfo::getEmptyKey(), Kind::Func, false, false,
false, 0, nullptr);
BackDeploymentKind::None, 0, nullptr);
}
static SILDeclRef getTombstoneKey() {
return SILDeclRef(PointerInfo::getTombstoneKey(), Kind::Func, false, false,
false, 0, nullptr);
BackDeploymentKind::None, 0, nullptr);
}
static unsigned getHashValue(swift::SILDeclRef Val) {
unsigned h1 = PointerInfo::getHashValue(Val.loc.getOpaqueValue());
Expand All @@ -576,7 +595,7 @@ template<> struct DenseMapInfo<swift::SILDeclRef> {
unsigned h4 = UnsignedInfo::getHashValue(Val.isForeign);
unsigned h5 = PointerInfo::getHashValue(Val.pointer.getOpaqueValue());
unsigned h6 = UnsignedInfo::getHashValue(Val.isDistributed);
unsigned h7 = UnsignedInfo::getHashValue(Val.isBackDeployed);
unsigned h7 = UnsignedInfo::getHashValue(unsigned(Val.backDeploymentKind));
return h1 ^ (h2 << 4) ^ (h3 << 9) ^ (h4 << 7) ^ (h5 << 11) ^ (h6 << 8) ^
(h7 << 10);
}
Expand Down
57 changes: 32 additions & 25 deletions lib/SIL/IR/SILDeclRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,18 @@ bool swift::requiresForeignEntryPoint(ValueDecl *vd) {
return false;
}

SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind,
bool isForeign, bool isDistributed, bool isBackDeployed,
SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind, bool isForeign,
bool isDistributed,
SILDeclRef::BackDeploymentKind backDeploymentKind,
AutoDiffDerivativeFunctionIdentifier *derivativeId)
: loc(vd), kind(kind), isForeign(isForeign), isDistributed(isDistributed),
isBackDeployed(isBackDeployed), defaultArgIndex(0), pointer(derivativeId) {}
backDeploymentKind(backDeploymentKind), defaultArgIndex(0),
pointer(derivativeId) {}

SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign,
bool asDistributed, bool asBackDeployed)
: defaultArgIndex(0),
bool asDistributed)
: backDeploymentKind(SILDeclRef::BackDeploymentKind::None),
defaultArgIndex(0),
pointer((AutoDiffDerivativeFunctionIdentifier *)nullptr) {
if (auto *vd = baseLoc.dyn_cast<ValueDecl*>()) {
if (auto *fd = dyn_cast<FuncDecl>(vd)) {
Expand Down Expand Up @@ -168,7 +171,6 @@ SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc, bool asForeign,

isForeign = asForeign;
isDistributed = asDistributed;
isBackDeployed = asBackDeployed;
}

SILDeclRef::SILDeclRef(SILDeclRef::Loc baseLoc,
Expand Down Expand Up @@ -205,7 +207,7 @@ ASTContext &SILDeclRef::getASTContext() const {

bool SILDeclRef::isThunk() const {
return isForeignToNativeThunk() || isNativeToForeignThunk() ||
isDistributedThunk() || isBackDeployedThunk();
isDistributedThunk() || isBackDeploymentThunk();
}

bool SILDeclRef::isClangImported() const {
Expand Down Expand Up @@ -317,9 +319,9 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {
return maybeAddExternal(SILLinkage::PublicNonABI);
}

// Back deployment thunks are emitted into the client and therefore have
// PublicNonABI linkage.
if (isBackDeployedThunk())
// Back deployment thunks and fallbacks are emitted into the client and
// therefore have PublicNonABI linkage.
if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None)
return maybeAddExternal(SILLinkage::PublicNonABI);

enum class Limit {
Expand Down Expand Up @@ -736,10 +738,8 @@ bool SILDeclRef::isAlwaysInline() const {
}

bool SILDeclRef::isAnyThunk() const {
return isForeignToNativeThunk() ||
isNativeToForeignThunk() ||
isDistributedThunk() ||
isBackDeployedThunk();
return isForeignToNativeThunk() || isNativeToForeignThunk() ||
isDistributedThunk() || isBackDeploymentThunk();
}

bool SILDeclRef::isForeignToNativeThunk() const {
Expand Down Expand Up @@ -793,8 +793,14 @@ bool SILDeclRef::isDistributedThunk() const {
return kind == Kind::Func;
}

bool SILDeclRef::isBackDeployedThunk() const {
if (!isBackDeployed)
bool SILDeclRef::isBackDeploymentFallback() const {
if (backDeploymentKind != BackDeploymentKind::Fallback)
return false;
return kind == Kind::Func;
}

bool SILDeclRef::isBackDeploymentThunk() const {
if (backDeploymentKind != BackDeploymentKind::Thunk)
return false;
return kind == Kind::Func;
}
Expand Down Expand Up @@ -879,8 +885,10 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const {
SKind = ASTMangler::SymbolKind::ObjCAsSwiftThunk;
} else if (isDistributedThunk()) {
SKind = ASTMangler::SymbolKind::DistributedThunk;
} else if (isBackDeployedThunk()) {
SKind = ASTMangler::SymbolKind::BackDeployedThunk;
} else if (isBackDeploymentThunk()) {
SKind = ASTMangler::SymbolKind::BackDeploymentThunk;
} else if (isBackDeploymentFallback()) {
SKind = ASTMangler::SymbolKind::BackDeploymentFallback;
}
break;
case SILDeclRef::ManglingKind::DynamicThunk:
Expand Down Expand Up @@ -1012,7 +1020,7 @@ bool SILDeclRef::requiresNewVTableEntry() const {
return false;
if (isDistributedThunk())
return false;
if (isBackDeployedThunk())
if (isBackDeploymentThunk())
return false;
auto fnDecl = dyn_cast<AbstractFunctionDecl>(getDecl());
if (!fnDecl)
Expand Down Expand Up @@ -1041,6 +1049,9 @@ SILDeclRef SILDeclRef::getOverridden() const {

SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const {
if (auto overridden = getOverridden()) {
// Back deployed methods should not be overridden.
assert(backDeploymentKind == SILDeclRef::BackDeploymentKind::None);

// If we overrode a foreign decl or dynamic method, if this is an
// accessor for a property that overrides an ObjC decl, or if it is an
// @NSManaged property, then it won't be in the vtable.
Expand All @@ -1051,10 +1062,6 @@ SILDeclRef SILDeclRef::getNextOverriddenVTableEntry() const {
if (isDistributedThunk())
return SILDeclRef();

// Back deployed thunks are not in the vtable.
if (isBackDeployedThunk())
return SILDeclRef();

// An @objc convenience initializer can be "overridden" in the sense that
// its selector is reclaimed by a subclass's convenience init with the
// same name. The AST models this as an override for the purposes of
Expand Down Expand Up @@ -1336,7 +1343,7 @@ bool SILDeclRef::canBeDynamicReplacement() const {
return false;
if (isDistributedThunk())
return false;
if (isBackDeployedThunk())
if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None)
return false;
if (kind == SILDeclRef::Kind::Destroyer ||
kind == SILDeclRef::Kind::DefaultArgGenerator)
Expand All @@ -1357,7 +1364,7 @@ bool SILDeclRef::isDynamicallyReplaceable() const {
if (isDistributedThunk())
return false;

if (isBackDeployedThunk())
if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None)
return false;

if (kind == SILDeclRef::Kind::DefaultArgGenerator)
Expand Down
4 changes: 2 additions & 2 deletions lib/SIL/Parser/ParseSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1519,7 +1519,7 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result,
if (!P.consumeIf(tok::sil_exclamation)) {
// Construct SILDeclRef.
Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false,
/*backDeployed=*/false, DerivativeId);
SILDeclRef::BackDeploymentKind::None, DerivativeId);
return false;
}

Expand Down Expand Up @@ -1640,7 +1640,7 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result,

// Construct SILDeclRef.
Result = SILDeclRef(VD, Kind, IsObjC, /*distributed=*/false,
/*backDeployed=*/false, DerivativeId);
SILDeclRef::BackDeploymentKind::None, DerivativeId);
return false;
}

Expand Down
20 changes: 14 additions & 6 deletions lib/SILGen/SILGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -853,18 +853,18 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
return;
}

if (constant.isBackDeployedThunk()) {
if (constant.isBackDeploymentThunk()) {
auto loc = constant.getAsRegularLocation();
loc.markAutoGenerated();
auto *dc = loc.getAsDeclContext();
assert(dc);

preEmitFunction(constant, f, loc);
PrettyStackTraceSILFunction X("silgen emitBackDeployedThunk", f);
PrettyStackTraceSILFunction X("silgen emitBackDeploymentThunk", f);
f->setBare(IsBare);
f->setThunk(IsThunk);

SILGenFunction(*this, *f, dc).emitBackDeployedThunk(constant);
SILGenFunction(*this, *f, dc).emitBackDeploymentThunk(constant);

postEmitFunction(constant, f);
return;
Expand Down Expand Up @@ -1402,9 +1402,17 @@ void SILGenModule::emitAbstractFuncDecl(AbstractFunctionDecl *AFD) {
}

if (AFD->isBackDeployed()) {
// FIXME(backDeploy): Emit client copy of back deployed function
auto thunk = SILDeclRef(AFD).asBackDeployed();
emitBackDeployedThunk(thunk);
// Emit the fallback function that will be used when the original function
// is unavailable at runtime.
auto fallback = SILDeclRef(AFD).asBackDeploymentKind(
SILDeclRef::BackDeploymentKind::Fallback);
emitFunctionDefinition(fallback, getFunction(fallback, ForDefinition));

// Emit the thunk that either invokes the original function or the fallback
// function depending on the availability of the original.
auto thunk = SILDeclRef(AFD).asBackDeploymentKind(
SILDeclRef::BackDeploymentKind::Thunk);
emitBackDeploymentThunk(thunk);
}
}

Expand Down
6 changes: 3 additions & 3 deletions lib/SILGen/SILGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
void emitDistributedThunk(SILDeclRef thunk);

/// Emits a thunk that calls either the original function if it is available
/// or otherwise calls a copy of the function that was emitted into the
/// calling module.
void emitBackDeployedThunk(SILDeclRef thunk);
/// or otherwise calls a fallback variant of the function that was emitted
/// into the client module.
void emitBackDeploymentThunk(SILDeclRef thunk);

void preEmitFunction(SILDeclRef constant, SILFunction *F, SILLocation L);
void postEmitFunction(SILDeclRef constant, SILFunction *F);
Expand Down
Loading

0 comments on commit 5339dae

Please sign in to comment.