[SIL] Track the parent module of a SIL global variable through serialization

As with SIL functions, track the parent module where a SIL global
variable was originally defined so that we can determine whether we
are outside of its original module for linkage purposes. Use this to
make sure we emit via a weak definition when emitting to a module
other than the originating module.

Fixes rdar://160153163.
This commit is contained in:
Doug Gregor
2025-09-08 16:03:33 -07:00
parent 33a059f689
commit 74e85c8416
8 changed files with 49 additions and 12 deletions

View File

@@ -51,7 +51,11 @@ private:
/// The SIL module that the global variable belongs to.
SILModule &Module;
/// The module that defines this global variable. This member should only be
/// when a global variable is deserialized to be emitted into another module.
ModuleDecl *ParentModule = nullptr;
/// The mangled name of the variable, which will be propagated to the
/// binary. A pointer into the module's lookup table.
StringRef Name;
@@ -120,6 +124,15 @@ public:
SILModule &getModule() const { return Module; }
/// Returns the module that defines this function.
ModuleDecl *getParentModule() const;
/// Sets \c ParentModule as fallback if \c DeclCtxt is not available to
/// provide the parent module.
void setParentModule(ModuleDecl *module) {
ParentModule = module;
}
SILType getLoweredType() const { return LoweredType; }
CanSILFunctionType getLoweredFunctionType() const {
return LoweredType.castTo<SILFunctionType>();

View File

@@ -43,6 +43,10 @@ SILGlobalVariable *SILGlobalVariable::create(SILModule &M, SILLinkage linkage,
return var;
}
ModuleDecl *SILGlobalVariable::getParentModule() const {
return ParentModule ? ParentModule : getModule().getSwiftModule();
}
static bool isGlobalLet(SILModule &mod, VarDecl *decl, SILType type) {
if (!decl)
return false;
@@ -92,11 +96,22 @@ bool SILGlobalVariable::isPossiblyUsedExternally() const {
}
bool SILGlobalVariable::hasNonUniqueDefinition() const {
auto decl = getDecl();
if (!decl)
// Non-uniqueness is a property of the Embedded linkage model.
auto &ctx = getModule().getASTContext();
if (!ctx.LangOpts.hasFeature(Feature::Embedded))
return false;
return SILDeclRef::declHasNonUniqueDefinition(decl);
// If this is for a declaration, ask it.
if (auto decl = getDecl()) {
return SILDeclRef::declHasNonUniqueDefinition(decl);
}
// If this variable is from a different module than the one we are emitting
// code for, then it must have a non-unique definition.
if (getParentModule() != getModule().getSwiftModule())
return true;
return false;
}
bool SILGlobalVariable::shouldBePreservedForDebugger() const {

View File

@@ -231,8 +231,6 @@ void SILGenModule::emitGlobalInitialization(PatternBindingDecl *pd,
auto onceSILTy
= SILType::getPrimitiveObjectType(onceTy->getCanonicalType());
// TODO: include the module in the onceToken's name mangling.
// Then we can make it fragile.
auto onceToken = SILGlobalVariable::create(M, SILLinkage::Private,
IsNotSerialized,
onceTokenBuffer, onceSILTy);

View File

@@ -4078,9 +4078,11 @@ SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) {
TypeID TyID;
DeclID dID;
ModuleID parentModuleID;
unsigned rawLinkage, serializedKind, IsDeclaration, IsLet, IsUsed;
SILGlobalVarLayout::readRecord(scratch, rawLinkage, serializedKind,
IsDeclaration, IsLet, IsUsed, TyID, dID);
IsDeclaration, IsLet, IsUsed, TyID, dID,
parentModuleID);
if (TyID == 0) {
LLVM_DEBUG(llvm::dbgs() << "SILGlobalVariable typeID is 0.\n");
return nullptr;
@@ -4115,6 +4117,9 @@ SILGlobalVariable *SILDeserializer::readGlobalVar(StringRef Name) {
globalVarOrOffset.set(v, true /*isFullyDeserialized*/);
v->setDeclaration(IsDeclaration);
if (parentModuleID)
v->setParentModule(MF->getModule(parentModuleID));
if (Callback)
Callback->didDeserialize(MF->getAssociatedModule(), v);

View File

@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
/// describe what change you made. The content of this comment isn't important;
/// it just ensures a conflict if two people change the module format.
/// Don't worry about adhering to the 80-column limit for this line.
const uint16_t SWIFTMODULE_VERSION_MINOR = 959; // Removed assign_by_wrapper SILGen instruction
const uint16_t SWIFTMODULE_VERSION_MINOR = 960; // SILGlobalVariable parent module
/// A standard hash seed used for all string hashes in a serialized module.
///

View File

@@ -296,7 +296,8 @@ namespace sil_block {
BCFixed<1>, // Is this a let variable.
BCFixed<1>, // Is this marked as "used".
TypeIDField,
DeclIDField
DeclIDField,
ModuleIDField // Parent ModuleDecl *
>;
using DifferentiabilityWitnessLayout = BCRecordLayout<

View File

@@ -3114,6 +3114,11 @@ void SILSerializer::writeSILGlobalVar(const SILGlobalVariable &g) {
GlobalVarOffset.push_back(Out.GetCurrentBitNo());
TypeID TyID = S.addTypeRef(g.getLoweredType().getRawASTType());
DeclID dID = S.addDeclRef(g.getDecl());
ModuleID parentModuleID;
if (auto *parentModule = g.getParentModule())
parentModuleID = S.addModuleRef(parentModule);
SILGlobalVarLayout::emitRecord(Out, ScratchRecord,
SILAbbrCodes[SILGlobalVarLayout::Code],
toStableSILLinkage(g.getLinkage()),
@@ -3121,7 +3126,7 @@ void SILSerializer::writeSILGlobalVar(const SILGlobalVariable &g) {
(unsigned)!g.isDefinition(),
(unsigned)g.isLet(),
(unsigned)g.markedAsUsed(),
TyID, dID);
TyID, dID, parentModuleID);
// Don't emit the initializer instructions if not marked as "serialized".
if (!g.isAnySerialized())

View File

@@ -1,8 +1,8 @@
// RUN: %empty-directory(%t)
// RUN: %{python} %utils/split_file.py -o %t %s
// RUN: %target-swift-frontend -mergeable-symbols -num-threads 2 -O -c -emit-module -o %t/MyModule.o %t/MyModule.swift -enable-experimental-feature Embedded -parse-as-library
// RUN: %target-swift-frontend -mergeable-symbols -num-threads 2 -O -c -o %t/MainA.o -o %t/MainB.o %t/MainA.swift %t/MainB.swift -I %t -enable-experimental-feature Embedded -parse-as-library
// RUN: %target-swift-frontend -num-threads 2 -O -c -emit-module -o %t/MyModule.o %t/MyModule.swift -enable-experimental-feature Embedded -parse-as-library
// RUN: %target-swift-frontend -num-threads 2 -O -c -o %t/MainA.o -o %t/MainB.o %t/MainA.swift %t/MainB.swift -I %t -enable-experimental-feature Embedded -parse-as-library
// RUN: %target-embedded-link %target-clang-resource-dir-opt %t/MainA.o %t/MainB.o %t/MyModule.o -o %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s