Skip to content

Thunk linkage and fragility cleanup #8422

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion include/swift/SIL/SILFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,8 @@ class SILFunction
/// Returns true if this function can be inlined into a fragile function
/// body.
bool hasValidLinkageForFragileInline() const {
return isSerialized() || isThunk() == IsReabstractionThunk;
return (isSerialized() == IsSerialized ||
isSerialized() == IsSerializable);
}

/// Returns true if this function can be referenced from a fragile function
Expand Down
8 changes: 6 additions & 2 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1878,11 +1878,15 @@ static bool isVersionedInternalDecl(const ValueDecl *VD) {
if (VD->getAttrs().hasAttribute<VersionedAttr>())
return true;

if (auto *fn = dyn_cast<FuncDecl>(VD))
if (auto *ASD = fn->getAccessorStorageDecl())
if (auto *FD = dyn_cast<FuncDecl>(VD))
if (auto *ASD = FD->getAccessorStorageDecl())
if (ASD->getAttrs().hasAttribute<VersionedAttr>())
return true;

if (auto *EED = dyn_cast<EnumElementDecl>(VD))
if (EED->getParentEnum()->getAttrs().hasAttribute<VersionedAttr>())
return true;

return false;
}

Expand Down
39 changes: 20 additions & 19 deletions lib/SIL/Linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,41 +182,42 @@ bool SILLinkerVisitor::visitApplyInst(ApplyInst *AI) {
if (!Callee)
return false;

// If the linking mode is not link all, AI is not transparent, and the
// callee is not shared, we don't want to perform any linking.
if (!isLinkAll() && !Callee->isTransparent() &&
!hasSharedVisibility(Callee->getLinkage()))
return false;
if (isLinkAll() ||
hasSharedVisibility(Callee->getLinkage())) {
addFunctionToWorklist(Callee);
return true;
}

// Otherwise we want to try and link in the callee... Add it to the callee
// list and return true.
addFunctionToWorklist(Callee);
return true;
return false;
}

bool SILLinkerVisitor::visitPartialApplyInst(PartialApplyInst *PAI) {
SILFunction *Callee = PAI->getReferencedFunction();
if (!Callee)
return false;
if (!isLinkAll() && !Callee->isTransparent() &&
!hasSharedVisibility(Callee->getLinkage()))
return false;

addFunctionToWorklist(Callee);
return true;
if (isLinkAll() ||
hasSharedVisibility(Callee->getLinkage())) {
addFunctionToWorklist(Callee);
return true;
}

return false;
}

bool SILLinkerVisitor::visitFunctionRefInst(FunctionRefInst *FRI) {
// Needed to handle closures which are no longer applied, but are left
// behind as dead code. This shouldn't happen, but if it does don't get into
// an inconsistent state.
SILFunction *Callee = FRI->getReferencedFunction();
if (!isLinkAll() && !Callee->isTransparent() &&
!hasSharedVisibility(Callee->getLinkage()))
return false;

addFunctionToWorklist(FRI->getReferencedFunction());
return true;
if (isLinkAll() ||
hasSharedVisibility(Callee->getLinkage())) {
addFunctionToWorklist(FRI->getReferencedFunction());
return true;
}

return false;
}

bool SILLinkerVisitor::visitProtocolConformance(
Expand Down
62 changes: 45 additions & 17 deletions lib/SIL/SILDeclRef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,24 +430,35 @@ SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {
return SILLinkage::Shared;
moduleContext = moduleContext->getParent();
}

// Currying and calling convention thunks have shared linkage.
if (isThunk())
// If a function declares a @_cdecl name, its native-to-foreign thunk
// is exported with the visibility of the function.
if (!isNativeToForeignThunk() || !d->getAttrs().hasAttribute<CDeclAttr>())
return SILLinkage::Shared;

// Enum constructors are essentially the same as thunks, they are

// Enum constructors and curry thunks either have private or shared
// linkage, dependings are essentially the same as thunks, they are
// emitted by need and have shared linkage.
if (isEnumElement())
if (isEnumElement() || isCurried) {
switch (d->getEffectiveAccess()) {
case Accessibility::Private:
case Accessibility::FilePrivate:
return (forDefinition
? SILLinkage::Private
: SILLinkage::PrivateExternal);

default:
return SILLinkage::Shared;
}
}

// Calling convention thunks have shared linkage.
if (isForeignToNativeThunk())
return SILLinkage::Shared;

// Declarations imported from Clang modules have shared linkage.
const SILLinkage ClangLinkage = SILLinkage::Shared;
// If a function declares a @_cdecl name, its native-to-foreign thunk
// is exported with the visibility of the function.
if (isNativeToForeignThunk() && !d->getAttrs().hasAttribute<CDeclAttr>())
return SILLinkage::Shared;

// Declarations imported from Clang modules have shared linkage.
if (isClangImported())
return ClangLinkage;
return SILLinkage::Shared;

// Otherwise, we have external linkage.
switch (d->getEffectiveAccess()) {
Expand Down Expand Up @@ -529,18 +540,30 @@ IsSerialized_t SILDeclRef::isSerialized() const {
if (auto closure = getAbstractClosureExpr())
dc = closure->getLocalContext();
else {
auto *d = getDecl();
dc = getDecl()->getInnermostDeclContext();

// Enum case constructors are serialized if the enum is @_versioned
// or public.
// Enum element constructors are serialized if the enum is
// @_versioned or public.
if (isEnumElement())
if (cast<EnumDecl>(dc)->getEffectiveAccess() >= Accessibility::Public)
if (d->getEffectiveAccess() >= Accessibility::Public)
return IsSerialized;

// Currying thunks are serialized if referenced from an inlinable
// context -- Sema's semantic checks ensure the serialization of
// such a thunk is valid, since it must in turn reference a public
// symbol, or dispatch via class_method or witness_method.
if (isCurried)
if (d->getEffectiveAccess() >= Accessibility::Public)
return IsSerializable;

if (isForeignToNativeThunk())
return IsSerializable;

// The allocating entry point for designated initializers are serialized
// if the class is @_versioned or public.
if (kind == SILDeclRef::Kind::Allocator) {
auto *ctor = cast<ConstructorDecl>(getDecl());
auto *ctor = cast<ConstructorDecl>(d);
if (ctor->isDesignatedInit() &&
ctor->getDeclContext()->getAsClassOrClassExtensionContext()) {
if (ctor->getEffectiveAccess() >= Accessibility::Public &&
Expand All @@ -550,6 +573,11 @@ IsSerialized_t SILDeclRef::isSerialized() const {
}
}

// Declarations imported from Clang modules are serialized if
// referenced from an inlineable context.
if (isClangImported())
return IsSerializable;

// Otherwise, ask the AST if we're inside an @_inlineable context.
if (dc->getResilienceExpansion() == ResilienceExpansion::Minimal)
return IsSerialized;
Expand Down
4 changes: 1 addition & 3 deletions lib/SIL/SILModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,7 @@ SILFunction *SILModule::getOrCreateFunction(SILLocation loc,
IsTransparent_t IsTrans = constant.isTransparent()
? IsTransparent
: IsNotTransparent;
IsSerialized_t IsSer = constant.isSerialized()
? IsSerialized
: IsNotSerialized;
IsSerialized_t IsSer = constant.isSerialized();

EffectsKind EK = constant.hasEffectsAttribute()
? constant.getEffectsAttribute()
Expand Down
12 changes: 0 additions & 12 deletions lib/SIL/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3903,18 +3903,6 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
void visitSILFunction(SILFunction *F) {
PrettyStackTraceSILFunction stackTrace("verifying", F);

if (F->getLinkage() == SILLinkage::PrivateExternal) {
// FIXME: uncomment these checks.
// <rdar://problem/18635841> SILGen can create non-fragile external
// private_external declarations
//
// assert(!isExternalDeclaration() &&
// "PrivateExternal should not be an external declaration");
// assert(isFragile() &&
// "PrivateExternal should be fragile (otherwise, how did it appear "
// "in this module?)");
}

CanSILFunctionType FTy = F->getLoweredFunctionType();
verifySILFunctionType(FTy);

Expand Down
11 changes: 3 additions & 8 deletions lib/SILGen/SILGenThunk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,9 @@ SILFunction *SILGenModule::getDynamicThunk(SILDeclRef constant,
// Mangle the constant with a _TTD header.
auto name = constant.mangle(SILDeclRef::ManglingKind::DynamicThunk);

IsSerialized_t isSerialized = IsNotSerialized;
if (makeModuleFragile)
isSerialized = IsSerialized;
if (constant.isSerialized())
isSerialized = IsSerialized;
auto F = M.getOrCreateFunction(constant.getDecl(), name, SILLinkage::Shared,
constantInfo.getSILType().castTo<SILFunctionType>(),
IsBare, IsTransparent, isSerialized, IsThunk);
constantInfo.SILFnType, IsBare, IsTransparent,
IsSerializable, IsThunk);

if (F->empty()) {
// Emit the thunk if we haven't yet.
Expand Down Expand Up @@ -283,6 +278,6 @@ getOrCreateReabstractionThunk(GenericEnvironment *genericEnv,

auto loc = RegularLocation::getAutoGeneratedLocation();
return M.getOrCreateSharedFunction(loc, name, thunkType, IsBare,
IsTransparent, Serialized,
IsTransparent, IsSerializable,
IsReabstractionThunk);
}
4 changes: 3 additions & 1 deletion lib/SILGen/SILGenType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,9 @@ SILGenModule::emitProtocolWitness(ProtocolConformance *conformance,
IsSerialized_t isSerialized = IsNotSerialized;
if (makeModuleFragile)
isSerialized = IsSerialized;
if (witnessRef.isSerialized())
if (witnessRef.isSerialized() &&
(hasSharedVisibility(linkage) ||
hasPublicVisibility(linkage)))
isSerialized = IsSerialized;

auto *f = M.createFunction(
Expand Down
11 changes: 4 additions & 7 deletions lib/Serialization/SerializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,10 @@ void SILSerializer::addReferencedSILFunction(const SILFunction *F,
return;
}

// If we referenced a non-fragile shared function from a fragile
// function, serialize it too. In practice, it will either be a
// thunk, or an optimizer specialization. In both cases, we don't
// have enough information at the time we emit the function to
// know if it should be marked fragile or not.
if (F->getLinkage() == SILLinkage::Shared && !DeclOnly) {
assert(F->isThunk() == IsReabstractionThunk || F->hasForeignBody());
assert(F->isSerialized() == IsSerializable ||
F->hasForeignBody());

FuncsToEmit[F] = false;
Worklist.push_back(F);
return;
Expand Down Expand Up @@ -1996,7 +1993,7 @@ bool SILSerializer::shouldEmitFunctionBody(const SILFunction *F,
return true;

// If F is serialized, we should always emit its body.
if (F->isSerialized())
if (F->isSerialized() == IsSerialized)
return true;

return false;
Expand Down
2 changes: 1 addition & 1 deletion test/ClangImporter/static_inline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// RUN: %FileCheck < %t/test.sil %s
// RUN: %target-swift-frontend -parse-as-library -module-name=test -O -emit-ir %t/test.sil -import-objc-header %S/Inputs/static_inline.h | %FileCheck --check-prefix=CHECK-IR %s

// CHECK: sil shared [clang c_inline_func] @c_inline_func : $@convention(c) (Int32) -> Int32
// CHECK: sil shared [serializable] [clang c_inline_func] @c_inline_func : $@convention(c) (Int32) -> Int32

// CHECK-IR-LABEL: define{{.*}} i32 @_T04test6testits5Int32VAD1x_tF(i32)
// CHECK-IR: = add {{.*}}, 27
Expand Down
12 changes: 6 additions & 6 deletions test/SILGen/c_materializeForSet_linkage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ extension NSReferencePoint: Pointable {}
// Make sure synthesized materializeForSet and its callbacks have shared linkage
// for properties imported from Clang

// CHECK-LABEL: sil shared [transparent] [serialized] @_T0SC7NSPointV1xSffm
// CHECK-LABEL: sil shared [transparent] [serialized] @_T0SC7NSPointV1ySffm
// CHECK-LABEL: sil shared [transparent] [serializable] @_T0SC7NSPointV1xSffm
// CHECK-LABEL: sil shared [transparent] [serializable] @_T0SC7NSPointV1ySffm

// CHECK-LABEL: sil shared @_T0So16NSReferencePointC1xSffmytfU_
// CHECK-LABEL: sil shared @_T0So16NSReferencePointC1xSffm
// CHECK-LABEL: sil shared [serializable] @_T0So16NSReferencePointC1xSffmytfU_
// CHECK-LABEL: sil shared [serializable] @_T0So16NSReferencePointC1xSffm

// CHECK-LABEL: sil shared @_T0So16NSReferencePointC1ySffmytfU_
// CHECK-LABEL: sil shared @_T0So16NSReferencePointC1ySffm
// CHECK-LABEL: sil shared [serializable] @_T0So16NSReferencePointC1ySffmytfU_
// CHECK-LABEL: sil shared [serializable] @_T0So16NSReferencePointC1ySffm
8 changes: 4 additions & 4 deletions test/SILGen/cf.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ protocol Impedance {

extension CCImpedance: Impedance {}

// CHECK-LABEL: sil hidden [transparent] [serialized] [thunk] @_T0SC11CCImpedanceV2cf9ImpedanceA2cDP4real9ComponentQzfgTW
// CHECK-LABEL: sil shared [transparent] [serialized] @_T0SC11CCImpedanceV4realSdfg
// CHECK-LABEL: sil hidden [transparent] [serialized] [thunk] @_T0SC11CCImpedanceV2cf9ImpedanceA2cDP4imag9ComponentQzfgTW
// CHECK-LABEL: sil shared [transparent] [serialized] @_T0SC11CCImpedanceV4imagSdfg
// CHECK-LABEL: sil hidden [transparent] [thunk] @_T0SC11CCImpedanceV2cf9ImpedanceA2cDP4real9ComponentQzfgTW
// CHECK-LABEL: sil shared [transparent] [serializable] @_T0SC11CCImpedanceV4realSdfg
// CHECK-LABEL: sil hidden [transparent] [thunk] @_T0SC11CCImpedanceV2cf9ImpedanceA2cDP4imag9ComponentQzfgTW
// CHECK-LABEL: sil shared [transparent] [serializable] @_T0SC11CCImpedanceV4imagSdfg

class MyMagnetism : CCMagnetismModel {
// CHECK-LABEL: sil hidden [thunk] @_T02cf11MyMagnetismC15getRefrigerator{{[_0-9a-zA-Z]*}}FTo : $@convention(objc_method) (MyMagnetism) -> @autoreleased CCRefrigerator
Expand Down
12 changes: 6 additions & 6 deletions test/SILGen/cf_members.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,37 +202,37 @@ public func foo(_ x: Double) {
}
// CHECK: } // end sil function '_T010cf_members3foo{{[_0-9a-zA-Z]*}}F'

// CHECK-LABEL: sil shared [thunk] @_T0SC7Struct1VABSd5value_tcfCTO
// CHECK-LABEL: sil shared [serializable] [thunk] @_T0SC7Struct1VABSd5value_tcfCTO
// CHECK: bb0([[X:%.*]] : $Double, [[SELF:%.*]] : $@thin Struct1.Type):
// CHECK: [[CFUNC:%.*]] = function_ref @IAMStruct1CreateSimple
// CHECK: [[RET:%.*]] = apply [[CFUNC]]([[X]])
// CHECK: return [[RET]]

// CHECK-LABEL: sil shared [thunk] @_T0SC7Struct1V9translateABSd7radians_tFTO
// CHECK-LABEL: sil shared [serializable] [thunk] @_T0SC7Struct1V9translateABSd7radians_tFTO
// CHECK: bb0([[X:%.*]] : $Double, [[SELF:%.*]] : $Struct1):
// CHECK: store [[SELF]] to [trivial] [[TMP:%.*]] :
// CHECK: [[CFUNC:%.*]] = function_ref @IAMStruct1Rotate
// CHECK: [[RET:%.*]] = apply [[CFUNC]]([[TMP]], [[X]])
// CHECK: return [[RET]]

// CHECK-LABEL: sil shared [thunk] @_T0SC7Struct1V5scaleABSdFTO
// CHECK-LABEL: sil shared [serializable] [thunk] @_T0SC7Struct1V5scaleABSdFTO
// CHECK: bb0([[X:%.*]] : $Double, [[SELF:%.*]] : $Struct1):
// CHECK: [[CFUNC:%.*]] = function_ref @IAMStruct1Scale
// CHECK: [[RET:%.*]] = apply [[CFUNC]]([[SELF]], [[X]])
// CHECK: return [[RET]]

// CHECK-LABEL: sil shared [thunk] @_T0SC7Struct1V12staticMethods5Int32VyFZTO
// CHECK-LABEL: sil shared [serializable] [thunk] @_T0SC7Struct1V12staticMethods5Int32VyFZTO
// CHECK: bb0([[SELF:%.*]] : $@thin Struct1.Type):
// CHECK: [[CFUNC:%.*]] = function_ref @IAMStruct1StaticMethod
// CHECK: [[RET:%.*]] = apply [[CFUNC]]()
// CHECK: return [[RET]]

// CHECK-LABEL: sil shared [thunk] @_T0SC7Struct1V13selfComesLastySd1x_tFTO
// CHECK-LABEL: sil shared [serializable] [thunk] @_T0SC7Struct1V13selfComesLastySd1x_tFTO
// CHECK: bb0([[X:%.*]] : $Double, [[SELF:%.*]] : $Struct1):
// CHECK: [[CFUNC:%.*]] = function_ref @IAMStruct1SelfComesLast
// CHECK: apply [[CFUNC]]([[X]], [[SELF]])

// CHECK-LABEL: sil shared [thunk] @_T0SC7Struct1V14selfComesThirdys5Int32V1a_Sf1bSd1xtFTO
// CHECK-LABEL: sil shared [serializable] [thunk] @_T0SC7Struct1V14selfComesThirdys5Int32V1a_Sf1bSd1xtFTO
// CHECK: bb0([[X:%.*]] : $Int32, [[Y:%.*]] : $Float, [[Z:%.*]] : $Double, [[SELF:%.*]] : $Struct1):
// CHECK: [[CFUNC:%.*]] = function_ref @IAMStruct1SelfComesThird
// CHECK: apply [[CFUNC]]([[X]], [[Y]], [[SELF]], [[Z]])
Expand Down
Loading