Skip to content

Commit 0d94fb1

Browse files
authored
Merge pull request #60414 from tshortli/weak-linked-import
AST/IRGen: Accept `@_weakLinked` on import decls to force weak linkage of symbols from a module
2 parents f2f4716 + 1778a76 commit 0d94fb1

20 files changed

+438
-17
lines changed

include/swift/AST/Attr.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,8 @@ DECL_ATTR(_clangImporterSynthesizedType, ClangImporterSynthesizedType,
456456
74)
457457
SIMPLE_DECL_ATTR(_weakLinked, WeakLinked,
458458
OnNominalType | OnAssociatedType | OnFunc | OnAccessor | OnVar |
459-
OnSubscript | OnConstructor | OnEnumElement | OnExtension | UserInaccessible |
459+
OnSubscript | OnConstructor | OnEnumElement | OnExtension | OnImport |
460+
UserInaccessible |
460461
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
461462
75)
462463
SIMPLE_DECL_ATTR(frozen, Frozen,

include/swift/AST/FileUnit.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@ class FileUnit : public DeclContext, public ASTAllocated<FileUnit> {
123123
const ModuleDecl *importedModule,
124124
SmallSetVector<Identifier, 4> &spiGroups) const {};
125125

126+
/// Checks whether this file imports \c module as \c @_weakLinked.
127+
virtual bool importsModuleAsWeakLinked(const ModuleDecl *module) const {
128+
// For source files, this should be overridden to inspect the import
129+
// declarations in the file. Other kinds of file units, like serialized
130+
// modules, can just use this default implementation since the @_weakLinked
131+
// attribute is not transitive. If module C is imported @_weakLinked by
132+
// module B, that does not imply that module A imports module C @_weakLinked
133+
// if it imports module B.
134+
return false;
135+
}
136+
126137
virtual Optional<Fingerprint>
127138
loadFingerprint(const IterableDeclContext *IDC) const { return None; }
128139

include/swift/AST/Import.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ enum class ImportFlags {
8484
/// concurrency.
8585
Preconcurrency = 0x20,
8686

87+
/// The module's symbols are linked weakly.
88+
WeakLinked = 0x40,
89+
8790
/// Used for DenseMap.
8891
Reserved = 0x80
8992
};

include/swift/AST/Module.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,12 +414,11 @@ class ModuleDecl
414414
/// present overlays as if they were part of their underlying module.
415415
std::pair<ModuleDecl *, Identifier> getDeclaringModuleAndBystander();
416416

417+
public:
417418
/// If this is a traditional (non-cross-import) overlay, get its underlying
418419
/// module if one exists.
419420
ModuleDecl *getUnderlyingModuleIfOverlay() const;
420421

421-
public:
422-
423422
/// Returns true if this module is an underscored cross import overlay
424423
/// declared by \p other or its underlying clang module, either directly or
425424
/// transitively (via intermediate cross-import overlays - for cross-imports
@@ -686,6 +685,10 @@ class ModuleDecl
686685
// Is \p spiGroup accessible as an explicitly imported SPI from this module?
687686
bool isImportedAsSPI(Identifier spiGroup, const ModuleDecl *fromModule) const;
688687

688+
/// Is \p targetDecl from a module that is imported as \c @_weakLinked from
689+
/// this module?
690+
bool isImportedAsWeakLinked(const Decl *targetDecl) const;
691+
689692
/// \sa getImportedModules
690693
enum class ImportFilterKind {
691694
/// Include imports declared with `@_exported`.

include/swift/AST/SourceFile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,9 @@ class SourceFile final : public FileUnit {
356356
const ModuleDecl *importedModule,
357357
llvm::SmallSetVector<Identifier, 4> &spiGroups) const override;
358358

359+
/// Is \p module imported as \c @_weakLinked by this file?
360+
bool importsModuleAsWeakLinked(const ModuleDecl *module) const override;
361+
359362
// Is \p targetDecl accessible as an explicitly imported SPI from this file?
360363
bool isImportedAsSPI(const ValueDecl *targetDecl) const;
361364

include/swift/SIL/SILFunction.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ enum IsDistributed_t {
6464
IsNotDistributed,
6565
IsDistributed,
6666
};
67+
enum IsWeakImported_t {
68+
IsNotWeakImported,
69+
IsWeakImportedByModule,
70+
IsAlwaysWeakImported,
71+
};
6772

6873
enum class PerformanceConstraints : uint8_t {
6974
None = 0,
@@ -317,9 +322,8 @@ class SILFunction
317322
/// would indicate.
318323
unsigned HasCReferences : 1;
319324

320-
/// Whether cross-module references to this function should always use
321-
/// weak linking.
322-
unsigned IsWeakImported : 1;
325+
/// Whether cross-module references to this function should use weak linking.
326+
unsigned IsWeakImported : 2;
323327

324328
/// Whether the implementation can be dynamically replaced.
325329
unsigned IsDynamicReplaceable : 1;
@@ -797,12 +801,18 @@ class SILFunction
797801

798802
/// Returns whether this function's symbol must always be weakly referenced
799803
/// across module boundaries.
800-
bool isAlwaysWeakImported() const { return IsWeakImported; }
804+
bool isAlwaysWeakImported() const {
805+
return IsWeakImported == IsWeakImported_t::IsAlwaysWeakImported;
806+
}
801807

802-
void setAlwaysWeakImported(bool value) {
803-
IsWeakImported = value;
808+
/// Returns whether this function's symbol was referenced by a module that
809+
/// imports the defining module \c @_weakLinked.
810+
bool isWeakImportedByModule() const {
811+
return IsWeakImported == IsWeakImported_t::IsWeakImportedByModule;
804812
}
805813

814+
void setIsWeakImported(IsWeakImported_t value) { IsWeakImported = value; }
815+
806816
bool isWeakImported() const;
807817

808818
/// Returns whether this function implementation can be dynamically replaced.

lib/AST/Decl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,9 @@ bool Decl::isWeakImported(ModuleDecl *fromModule) const {
10021002
if (isAlwaysWeakImported())
10031003
return true;
10041004

1005+
if (fromModule->isImportedAsWeakLinked(this))
1006+
return true;
1007+
10051008
auto availability = getAvailabilityForLinkage();
10061009
if (availability.isAlwaysAvailable())
10071010
return false;

lib/AST/Module.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2574,6 +2574,25 @@ bool SourceFile::isImportedAsSPI(const ValueDecl *targetDecl) const {
25742574
return false;
25752575
}
25762576

2577+
bool SourceFile::importsModuleAsWeakLinked(const ModuleDecl *module) const {
2578+
for (auto &import : *Imports) {
2579+
if (!import.options.contains(ImportFlags::WeakLinked))
2580+
continue;
2581+
2582+
const ModuleDecl *importedModule = import.module.importedModule;
2583+
if (module == importedModule)
2584+
return true;
2585+
2586+
// Also check whether the target module is actually the underlyingClang
2587+
// module for this @_weakLinked import.
2588+
const ModuleDecl *clangModule =
2589+
importedModule->getUnderlyingModuleIfOverlay();
2590+
if (module == clangModule)
2591+
return true;
2592+
}
2593+
return false;
2594+
}
2595+
25772596
bool ModuleDecl::isImportedAsSPI(const SpecializeAttr *attr,
25782597
const ValueDecl *targetDecl) const {
25792598
auto targetModule = targetDecl->getModuleContext();
@@ -2599,6 +2618,15 @@ bool ModuleDecl::isImportedAsSPI(Identifier spiGroup,
25992618
return importedSPIGroups.count(spiGroup);
26002619
}
26012620

2621+
bool ModuleDecl::isImportedAsWeakLinked(const Decl *targetDecl) const {
2622+
const auto *declaringModule = targetDecl->getModuleContext();
2623+
for (auto file : getFiles()) {
2624+
if (file->importsModuleAsWeakLinked(declaringModule))
2625+
return true;
2626+
}
2627+
return false;
2628+
}
2629+
26022630
bool Decl::isSPI() const {
26032631
return !getSPIGroups().empty();
26042632
}

lib/IRGen/GenDecl.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2487,10 +2487,23 @@ void IRGenModule::emitGlobalDecl(Decl *D) {
24872487
Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
24882488
const TypeInfo &ti,
24892489
ForDefinition_t forDefinition) {
2490+
LinkEntity entity = LinkEntity::forSILGlobalVariable(var, *this);
2491+
LinkInfo link = LinkInfo::get(*this, entity, forDefinition);
2492+
24902493
if (auto clangDecl = var->getClangDecl()) {
24912494
auto addr = getAddrOfClangGlobalDecl(cast<clang::VarDecl>(clangDecl),
24922495
forDefinition);
24932496

2497+
// Override the linkage computed by Clang if the decl is from another
2498+
// module that imported @_weakLinked.
2499+
//
2500+
// FIXME: We should be able to set the linkage unconditionally here but
2501+
// some fixes are needed for Cxx interop.
2502+
if (auto globalVar = dyn_cast<llvm::GlobalVariable>(addr)) {
2503+
if (getSwiftModule()->isImportedAsWeakLinked(var->getDecl()))
2504+
globalVar->setLinkage(link.getLinkage());
2505+
}
2506+
24942507
// If we're not emitting this to define it, make sure we cast it to the
24952508
// right type.
24962509
if (!forDefinition) {
@@ -2503,7 +2516,6 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
25032516
return Address(addr, alignment);
25042517
}
25052518

2506-
LinkEntity entity = LinkEntity::forSILGlobalVariable(var, *this);
25072519
ResilienceExpansion expansion = getResilienceExpansionForLayout(var);
25082520

25092521
llvm::Type *storageType;
@@ -2549,7 +2561,6 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
25492561

25502562
// Check whether we've created the global variable already.
25512563
// FIXME: We should integrate this into the LinkEntity cache more cleanly.
2552-
LinkInfo link = LinkInfo::get(*this, entity, forDefinition);
25532564
auto gvar = Module.getGlobalVariable(link.getName(), /*allowInternal*/ true);
25542565
if (gvar) {
25552566
if (forDefinition)
@@ -3222,6 +3233,7 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
32223233
}
32233234
}
32243235

3236+
LinkInfo link = LinkInfo::get(*this, entity, forDefinition);
32253237
bool isDefinition = f->isDefinition();
32263238
bool hasOrderNumber =
32273239
isDefinition && !shouldCallPreviousImplementation;
@@ -3242,8 +3254,16 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
32423254
if (clangAddr) {
32433255
fn = dyn_cast<llvm::Function>(clangAddr->stripPointerCasts());
32443256

3245-
// If we have a function, move it to the appropriate position.
32463257
if (fn) {
3258+
// Override the linkage computed by Clang if the decl is from another
3259+
// module that imported @_weakLinked.
3260+
//
3261+
// FIXME: We should be able to set the linkage unconditionally here but
3262+
// some fixes are needed for Cxx interop.
3263+
if (!forDefinition && f->isWeakImportedByModule())
3264+
fn->setLinkage(link.getLinkage());
3265+
3266+
// If we have a function, move it to the appropriate position.
32473267
if (hasOrderNumber) {
32483268
auto &fnList = Module.getFunctionList();
32493269
fnList.remove(fn);
@@ -3264,8 +3284,6 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
32643284
getSignature(f->getLoweredFunctionType(), fpKind);
32653285
addLLVMFunctionAttributes(f, signature);
32663286

3267-
LinkInfo link = LinkInfo::get(*this, entity, forDefinition);
3268-
32693287
fn = createFunction(*this, link, signature, insertBefore,
32703288
f->getOptimizationMode(), shouldEmitStackProtector(f));
32713289

lib/SIL/IR/SILFunction.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,9 @@ bool SILFunction::isTypeABIAccessible(SILType type) const {
453453
}
454454

455455
bool SILFunction::isWeakImported() const {
456+
if (isWeakImportedByModule())
457+
return true;
458+
456459
// For imported functions check the Clang declaration.
457460
if (ClangNodeOwner)
458461
return ClangNodeOwner->getClangDecl()->isWeakImported();

lib/SIL/IR/SILFunctionBuilder.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,14 @@ SILFunction *SILFunctionBuilder::getOrCreateFunction(
312312
F->setClangNodeOwner(decl);
313313

314314
F->setAvailabilityForLinkage(decl->getAvailabilityForLinkage());
315-
F->setAlwaysWeakImported(decl->isAlwaysWeakImported());
315+
316+
if (decl->isAlwaysWeakImported()) {
317+
F->setIsWeakImported(IsWeakImported_t::IsAlwaysWeakImported);
318+
} else if (decl->isWeakImported(mod.getSwiftModule())) {
319+
F->setIsWeakImported(IsWeakImported_t::IsWeakImportedByModule);
320+
} else {
321+
F->setIsWeakImported(IsWeakImported_t::IsNotWeakImported);
322+
}
316323

317324
if (auto *accessor = dyn_cast<AccessorDecl>(decl)) {
318325
auto *storage = accessor->getStorage();

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6483,7 +6483,9 @@ bool SILParserState::parseDeclSIL(Parser &P) {
64836483
if (!objCReplacementFor.empty())
64846484
FunctionState.F->setObjCReplacement(objCReplacementFor);
64856485
FunctionState.F->setSpecialPurpose(specialPurpose);
6486-
FunctionState.F->setAlwaysWeakImported(isWeakImported);
6486+
FunctionState.F->setIsWeakImported(
6487+
isWeakImported ? IsWeakImported_t::IsAlwaysWeakImported
6488+
: IsWeakImported_t::IsNotWeakImported);
64876489
FunctionState.F->setAvailabilityForLinkage(availability);
64886490
FunctionState.F->setWithoutActuallyEscapingThunk(
64896491
isWithoutActuallyEscapingThunk);

lib/Sema/ImportResolution.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,9 @@ UnboundImport::UnboundImport(ImportDecl *ID)
554554
import.options |= ImportFlags::Preconcurrency;
555555
import.preconcurrencyRange = attr->getRangeWithAt();
556556
}
557+
558+
if (auto attr = ID->getAttrs().getAttribute<WeakLinkedAttr>())
559+
import.options |= ImportFlags::WeakLinked;
557560
}
558561

559562
bool UnboundImport::checkNotTautological(const SourceFile &SF) {

lib/Serialization/DeserializeSIL.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,9 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn,
661661
fn->setEffectsKind(EffectsKind(effect));
662662
fn->setOptimizationMode(OptimizationMode(optimizationMode));
663663
fn->setPerfConstraints((PerformanceConstraints)perfConstr);
664-
fn->setAlwaysWeakImported(isWeakImported);
664+
fn->setIsWeakImported(isWeakImported
665+
? IsWeakImported_t::IsAlwaysWeakImported
666+
: IsWeakImported_t::IsNotWeakImported);
665667
fn->setClassSubclassScope(SubclassScope(subclassScope));
666668
fn->setHasCReferences(bool(hasCReferences));
667669

test/IRGen/Inputs/usr/include/BridgeTestFoundation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,13 @@ __attribute__((availability(macosx,introduced=10.51)))
5353
@interface NSUserNotificationAction : NSObject
5454
@end
5555

56+
void always_available_function();
57+
5658
__attribute__((availability(macosx,introduced=10.51)))
5759
void future_function_should_be_weak();
5860

5961
extern int weak_variable __attribute__((weak_import));
62+
extern int strong_variable;
6063

6164
@interface NSError : NSObject
6265

0 commit comments

Comments
 (0)