Skip to content

Commit 0dc768a

Browse files
authored
Merge pull request #61295 from hamishknight/linked-in
2 parents 8424b88 + 28aec55 commit 0dc768a

File tree

4 files changed

+244
-139
lines changed

4 files changed

+244
-139
lines changed

include/swift/SIL/SILDeclRef.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,10 @@ struct SILDeclRef {
380380
bool isAlwaysInline() const;
381381
/// True if the function has the @_backDeploy attribute.
382382
bool isBackDeployed() const;
383-
383+
384+
/// Return the expected linkage for a definition of this declaration.
385+
SILLinkage getDefinitionLinkage() const;
386+
384387
/// Return the expected linkage of this declaration.
385388
SILLinkage getLinkage(ForDefinition_t forDefinition) const;
386389

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 193 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -284,191 +284,251 @@ bool SILDeclRef::isImplicit() const {
284284
llvm_unreachable("Unhandled case in switch");
285285
}
286286

287-
SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {
288-
289-
// Prespecializations are public.
290-
if (getSpecializedSignature()) {
291-
return SILLinkage::Public;
292-
}
293-
294-
if (getAbstractClosureExpr()) {
295-
return isSerialized() ? SILLinkage::Shared : SILLinkage::Private;
296-
}
297-
298-
// The main entry-point is public.
299-
if (kind == Kind::EntryPoint)
300-
return SILLinkage::Public;
301-
if (kind == Kind::AsyncEntryPoint)
302-
return SILLinkage::Hidden;
303-
304-
// Add External to the linkage (e.g. Public -> PublicExternal) if this is a
305-
// declaration not a definition.
306-
auto maybeAddExternal = [&](SILLinkage linkage) {
307-
return forDefinition ? linkage : addExternalToLinkage(linkage);
308-
};
309-
310-
ValueDecl *d = getDecl();
311-
312-
// Property wrapper generators of public functions have PublicNonABI linkage
313-
if (isPropertyWrapperBackingInitializer() && isa<ParamDecl>(d)) {
314-
if (isSerialized())
315-
return maybeAddExternal(SILLinkage::PublicNonABI);
316-
}
317-
318-
// Function-local declarations have private linkage, unless serialized.
319-
DeclContext *moduleContext = d->getDeclContext();
320-
while (!moduleContext->isModuleScopeContext()) {
321-
if (moduleContext->isLocalContext()) {
322-
return isSerialized() ? SILLinkage::Shared : SILLinkage::Private;
287+
namespace {
288+
enum class LinkageLimit {
289+
/// No limit.
290+
None,
291+
/// The linkage should behave as if the decl is private.
292+
Private,
293+
/// The declaration is emitted on-demand; it should end up with internal
294+
/// or shared linkage.
295+
OnDemand,
296+
/// The declaration should never be made public.
297+
NeverPublic,
298+
/// The declaration should always be emitted into the client,
299+
AlwaysEmitIntoClient,
300+
};
301+
} // end anonymous namespace
302+
303+
/// Compute the linkage limit for a given SILDeclRef. This augments the
304+
/// mapping of access level to linkage to provide a maximum or minimum linkage.
305+
static LinkageLimit getLinkageLimit(SILDeclRef constant) {
306+
using Limit = LinkageLimit;
307+
using Kind = SILDeclRef::Kind;
308+
309+
auto *d = constant.getDecl();
310+
311+
// Back deployment thunks and fallbacks are emitted into the client.
312+
if (constant.backDeploymentKind != SILDeclRef::BackDeploymentKind::None)
313+
return Limit::AlwaysEmitIntoClient;
314+
315+
if (auto *fn = dyn_cast<AbstractFunctionDecl>(d)) {
316+
// Native-to-foreign thunks for top-level decls are created on-demand,
317+
// unless they are marked @_cdecl, in which case they expose a dedicated
318+
// entry-point with the visibility of the function.
319+
//
320+
// Native-to-foreign thunks for methods are always just private, since
321+
// they're anchored by Objective-C metadata.
322+
auto &attrs = fn->getAttrs();
323+
if (constant.isNativeToForeignThunk() && !attrs.hasAttribute<CDeclAttr>()) {
324+
auto isTopLevel = fn->getDeclContext()->isModuleScopeContext();
325+
return isTopLevel ? Limit::OnDemand : Limit::Private;
323326
}
324-
moduleContext = moduleContext->getParent();
325327
}
326328

327-
// Calling convention thunks have shared linkage.
328-
if (isForeignToNativeThunk())
329-
return SILLinkage::Shared;
330-
331-
// Declarations imported from Clang modules have shared linkage.
332-
if (isClangImported())
333-
return SILLinkage::Shared;
334-
335-
// Default argument generators of Public functions have PublicNonABI linkage
336-
// if the function was type-checked in Swift 4 mode.
337-
if (kind == SILDeclRef::Kind::DefaultArgGenerator) {
338-
if (isSerialized())
339-
return maybeAddExternal(SILLinkage::PublicNonABI);
329+
if (auto fn = constant.getFuncDecl()) {
330+
// Forced-static-dispatch functions are created on-demand and have
331+
// at best shared linkage.
332+
if (fn->hasForcedStaticDispatch())
333+
return Limit::OnDemand;
340334
}
341335

342-
// Back deployment thunks and fallbacks are emitted into the client and
343-
// therefore have PublicNonABI linkage.
344-
if (backDeploymentKind != SILDeclRef::BackDeploymentKind::None)
345-
return maybeAddExternal(SILLinkage::PublicNonABI);
346-
347-
enum class Limit {
348-
/// No limit.
349-
None,
350-
/// The declaration is emitted on-demand; it should end up with internal
351-
/// or shared linkage.
352-
OnDemand,
353-
/// The declaration should never be made public.
354-
NeverPublic,
355-
/// The declaration should always be emitted into the client,
356-
AlwaysEmitIntoClient,
357-
};
358-
auto limit = Limit::None;
359-
360-
// @_alwaysEmitIntoClient declarations are like the default arguments of
361-
// public functions; they are roots for dead code elimination and have
362-
// serialized bodies, but no public symbol in the generated binary.
363-
if (d->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
364-
limit = Limit::AlwaysEmitIntoClient;
365-
if (auto accessor = dyn_cast<AccessorDecl>(d)) {
366-
auto *storage = accessor->getStorage();
367-
if (storage->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
368-
limit = Limit::AlwaysEmitIntoClient;
336+
switch (constant.kind) {
337+
case Kind::Func:
338+
case Kind::Allocator:
339+
case Kind::Initializer:
340+
case Kind::Deallocator:
341+
case Kind::Destroyer: {
342+
// @_alwaysEmitIntoClient declarations are like the default arguments of
343+
// public functions; they are roots for dead code elimination and have
344+
// serialized bodies, but no public symbol in the generated binary.
345+
if (d->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
346+
return Limit::AlwaysEmitIntoClient;
347+
if (auto accessor = dyn_cast<AccessorDecl>(d)) {
348+
auto *storage = accessor->getStorage();
349+
if (storage->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
350+
return Limit::AlwaysEmitIntoClient;
351+
}
352+
break;
369353
}
370-
371-
// ivar initializers and destroyers are completely contained within the class
372-
// from which they come, and never get seen externally.
373-
if (isIVarInitializerOrDestroyer()) {
374-
limit = Limit::NeverPublic;
354+
case Kind::EnumElement:
355+
return Limit::OnDemand;
356+
357+
case Kind::GlobalAccessor:
358+
return cast<VarDecl>(d)->isResilient() ? Limit::NeverPublic : Limit::None;
359+
360+
case Kind::DefaultArgGenerator:
361+
// If the default argument is to be serialized, only use non-ABI public
362+
// linkage. If the argument is not to be serialized, don't use a limit.
363+
// This actually means that default arguments *can be ABI public* if
364+
// `isSerialized()` returns false and the effective access level is public,
365+
// which happens under `-enable-testing` with an internal decl.
366+
return constant.isSerialized() ? Limit::AlwaysEmitIntoClient : Limit::None;
367+
368+
case Kind::PropertyWrapperBackingInitializer:
369+
case Kind::PropertyWrapperInitFromProjectedValue: {
370+
if (!d->getDeclContext()->isTypeContext()) {
371+
// If the backing initializer is to be serialized, only use non-ABI public
372+
// linkage. If the initializer is not to be serialized, don't use a limit.
373+
// This actually means that it *can be ABI public* if `isSerialized()`
374+
// returns false and the effective access level is public, which happens
375+
// under `-enable-testing` with an internal decl.
376+
return constant.isSerialized() ? Limit::AlwaysEmitIntoClient
377+
: Limit::None;
378+
}
379+
// Otherwise, regular property wrapper backing initializers (for properties)
380+
// are treated just like stored property intializers.
381+
LLVM_FALLTHROUGH;
375382
}
376-
377-
// Stored property initializers get the linkage of their containing type.
378-
if (isStoredPropertyInitializer() || isPropertyWrapperBackingInitializer()) {
379-
// Three cases:
383+
case Kind::StoredPropertyInitializer: {
384+
// Stored property initializers get the linkage of their containing type.
385+
// There are three cases:
380386
//
381387
// 1) Type is formally @_fixed_layout/@frozen. Root initializers can be
382388
// declared @inlinable. The property initializer must only reference
383389
// public symbols, and is serialized, so we give it PublicNonABI linkage.
384390
//
385391
// 2) Type is not formally @_fixed_layout/@frozen and the module is not
386-
// resilient. Root initializers can be declared @inlinable. This is the
392+
// resilient. Root initializers can be declared @inlinable. This is the
387393
// annoying case. We give the initializer public linkage if the type is
388394
// public.
389395
//
390396
// 3) Type is resilient. The property initializer is never public because
391397
// root initializers cannot be @inlinable.
392398
//
393399
// FIXME: Get rid of case 2 somehow.
394-
if (isSerialized())
395-
return maybeAddExternal(SILLinkage::PublicNonABI);
396-
397-
d = cast<NominalTypeDecl>(d->getDeclContext());
400+
if (constant.isSerialized())
401+
return Limit::AlwaysEmitIntoClient;
398402

399403
// FIXME: This should always be true.
400404
if (d->getModuleContext()->isResilient())
401-
limit = Limit::NeverPublic;
402-
}
405+
return Limit::NeverPublic;
403406

404-
// The global addressor is never public for resilient globals.
405-
if (kind == Kind::GlobalAccessor) {
406-
if (cast<VarDecl>(d)->isResilient()) {
407-
limit = Limit::NeverPublic;
408-
}
407+
break;
409408
}
410-
411-
if (auto fn = dyn_cast<FuncDecl>(d)) {
412-
// Forced-static-dispatch functions are created on-demand and have
413-
// at best shared linkage.
414-
if (fn->hasForcedStaticDispatch()) {
415-
limit = Limit::OnDemand;
416-
}
409+
case Kind::IVarInitializer:
410+
case Kind::IVarDestroyer:
411+
// ivar initializers and destroyers are completely contained within the
412+
// class from which they come, and never get seen externally.
413+
return Limit::NeverPublic;
414+
415+
case Kind::EntryPoint:
416+
case Kind::AsyncEntryPoint:
417+
llvm_unreachable("Already handled");
417418
}
419+
return Limit::None;
420+
}
418421

419-
if (auto fn = dyn_cast<AbstractFunctionDecl>(d)) {
420-
// Native-to-foreign thunks for top-level decls are created on-demand,
421-
// unless they are marked @_cdecl, in which case they expose a dedicated
422-
// entry-point with the visibility of the function.
423-
//
424-
// Native-to-foreign thunks for methods are always just private, since
425-
// they're anchored by Objective-C metadata.
426-
if (isNativeToForeignThunk() && !fn->getAttrs().hasAttribute<CDeclAttr>()) {
427-
if (fn->getDeclContext()->isModuleScopeContext())
428-
limit = Limit::OnDemand;
429-
else
430-
return SILLinkage::Private;
422+
SILLinkage SILDeclRef::getDefinitionLinkage() const {
423+
using Limit = LinkageLimit;
424+
425+
auto privateLinkage = [&]() {
426+
// Private decls may still be serialized if they are e.g in an inlinable
427+
// function. In such a case, they receive shared linkage.
428+
return isSerialized() ? SILLinkage::Shared : SILLinkage::Private;
429+
};
430+
431+
// Prespecializations are public.
432+
if (getSpecializedSignature())
433+
return SILLinkage::Public;
434+
435+
// Closures can only be referenced from the same file.
436+
if (getAbstractClosureExpr())
437+
return privateLinkage();
438+
439+
// The main entry-point is public.
440+
if (kind == Kind::EntryPoint)
441+
return SILLinkage::Public;
442+
if (kind == Kind::AsyncEntryPoint)
443+
return SILLinkage::Hidden;
444+
445+
// Calling convention thunks have shared linkage.
446+
if (isForeignToNativeThunk())
447+
return SILLinkage::Shared;
448+
449+
// Declarations imported from Clang modules have shared linkage.
450+
if (isClangImported())
451+
return SILLinkage::Shared;
452+
453+
const auto limit = getLinkageLimit(*this);
454+
if (limit == Limit::Private)
455+
return privateLinkage();
456+
457+
auto *decl = getDecl();
458+
459+
if (isPropertyWrapperBackingInitializer()) {
460+
auto *dc = decl->getDeclContext();
461+
462+
// External property wrapper backing initializers have linkage based
463+
// on the access level of their function.
464+
if (isa<ParamDecl>(decl)) {
465+
if (isa<AbstractClosureExpr>(dc))
466+
return privateLinkage();
467+
468+
decl = cast<ValueDecl>(dc->getAsDecl());
431469
}
432-
}
433470

434-
if (isEnumElement()) {
435-
limit = Limit::OnDemand;
471+
// Property wrappers in types have linkage based on the access level of
472+
// their nominal.
473+
if (dc->isTypeContext())
474+
decl = cast<NominalTypeDecl>(dc);
436475
}
437476

438-
auto effectiveAccess = d->getEffectiveAccess();
439-
477+
// Stored property initializers have linkage based on the access level of
478+
// their nominal.
479+
if (isStoredPropertyInitializer())
480+
decl = cast<NominalTypeDecl>(decl->getDeclContext());
481+
482+
// Compute the effective access level, taking e.g testable into consideration.
483+
auto effectiveAccess = decl->getEffectiveAccess();
484+
440485
// Private setter implementations for an internal storage declaration should
441486
// be at least internal as well, so that a dynamically-writable
442487
// keypath can be formed from other files in the same module.
443-
if (auto accessor = dyn_cast<AccessorDecl>(d)) {
444-
if (accessor->isSetter()
445-
&& accessor->getStorage()->getEffectiveAccess() >= AccessLevel::Internal)
488+
if (auto *accessor = dyn_cast<AccessorDecl>(decl)) {
489+
auto storageAccess = accessor->getStorage()->getEffectiveAccess();
490+
if (accessor->isSetter() && storageAccess >= AccessLevel::Internal)
446491
effectiveAccess = std::max(effectiveAccess, AccessLevel::Internal);
447492
}
448493

449494
switch (effectiveAccess) {
450495
case AccessLevel::Private:
451496
case AccessLevel::FilePrivate:
452-
return SILLinkage::Private;
497+
return privateLinkage();
453498

454499
case AccessLevel::Internal:
500+
assert(!isSerialized() &&
501+
"Serialized decls should either be private (for decls in inlinable "
502+
"code), or they should be public");
455503
if (limit == Limit::OnDemand)
456504
return SILLinkage::Shared;
457-
return maybeAddExternal(SILLinkage::Hidden);
505+
return SILLinkage::Hidden;
458506

459507
case AccessLevel::Public:
460508
case AccessLevel::Open:
461-
if (limit == Limit::OnDemand)
509+
switch (limit) {
510+
case Limit::None:
511+
return SILLinkage::Public;
512+
case Limit::AlwaysEmitIntoClient:
513+
return SILLinkage::PublicNonABI;
514+
case Limit::OnDemand:
462515
return SILLinkage::Shared;
463-
if (limit == Limit::NeverPublic)
464-
return maybeAddExternal(SILLinkage::Hidden);
465-
if (limit == Limit::AlwaysEmitIntoClient)
466-
return maybeAddExternal(SILLinkage::PublicNonABI);
467-
return maybeAddExternal(SILLinkage::Public);
516+
case Limit::NeverPublic:
517+
return SILLinkage::Hidden;
518+
case Limit::Private:
519+
llvm_unreachable("Already handled");
520+
}
468521
}
469522
llvm_unreachable("unhandled access");
470523
}
471524

525+
SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {
526+
// Add external to the linkage of the definition
527+
// (e.g. Public -> PublicExternal) if this is a declaration.
528+
auto linkage = getDefinitionLinkage();
529+
return forDefinition ? linkage : addExternalToLinkage(linkage);
530+
}
531+
472532
SILDeclRef SILDeclRef::getDefaultArgGenerator(Loc loc,
473533
unsigned defaultArgIndex) {
474534
SILDeclRef result;

0 commit comments

Comments
 (0)