[IRGen] Lazily emit SIL global variables

Delay the emission of SIL global variables that aren't externally
visible until they are actually used. This is the same lazy emission
approach that we use for a number of other entities, such as SIL
functions.

Part of rdar://158363967.
This commit is contained in:
Doug Gregor
2025-09-04 13:03:15 -07:00
parent bee2369212
commit 4009814783
8 changed files with 106 additions and 13 deletions

View File

@@ -1174,6 +1174,26 @@ static bool isLazilyEmittedFunction(SILFunction &f, SILModule &m) {
return true;
}
// Eagerly emit global variables that are externally visible.
static bool isLazilyEmittedGlobalVariable(SILGlobalVariable &v, SILModule &m) {
// FIXME: Eagerly emit statically-initialized objects due to an issue I
// have yet to debug.
if (v.isInitializedObject())
return false;
if (v.isPossiblyUsedExternally()) {
// Under the embedded linkage model, if it has a non-unique definition,
// treat it lazily.
if (v.hasNonUniqueDefinition() && !v.markedAsUsed()) {
return true;
}
return false;
}
return true;
}
void IRGenerator::emitGlobalTopLevel(
const std::vector<std::string> &linkerDirectives) {
if (PrimaryIGM->getSILModule().getOptions().StopOptimizationAfterSerialization) {
@@ -1207,6 +1227,9 @@ void IRGenerator::emitGlobalTopLevel(
createLinkerDirectiveVariable(*PrimaryIGM, directive);
}
for (SILGlobalVariable &v : PrimaryIGM->getSILModule().getSILGlobals()) {
if (isLazilyEmittedGlobalVariable(v, PrimaryIGM->getSILModule()))
continue;
Decl *decl = v.getDecl();
CurrentIGMPtr IGM = getGenModule(decl ? decl->getDeclContext() : nullptr);
IGM->emitSILGlobalVariable(&v);
@@ -1368,6 +1391,7 @@ void IRGenerator::emitLazyDefinitions() {
!LazyExtensionDescriptors.empty() ||
!LazyFieldDescriptors.empty() ||
!LazyFunctionDefinitions.empty() || !LazyWitnessTables.empty() ||
!LazyGlobalVariables.empty() ||
!LazyCanonicalSpecializedMetadataAccessors.empty() ||
!LazyMetadataAccessors.empty() ||
!LazyClassMetadata.empty() ||
@@ -1456,6 +1480,13 @@ void IRGenerator::emitLazyDefinitions() {
IGM->emitSILFunction(f);
}
// Emit any lazy global variables we require.
while (!LazyGlobalVariables.empty()) {
SILGlobalVariable *v = LazyGlobalVariables.pop_back_val();
CurrentIGMPtr IGM = getGenModule(v);
IGM->emitSILGlobalVariable(v);
}
while (!LazyCanonicalSpecializedMetadataAccessors.empty()) {
CanType theType =
LazyCanonicalSpecializedMetadataAccessors.pop_back_val();
@@ -1529,6 +1560,26 @@ void IRGenerator::addLazyFunction(SILFunction *f) {
DefaultIGMForFunction.insert(std::make_pair(f, CurrentIGM));
}
void IRGenerator::addLazyGlobalVariable(SILGlobalVariable *v) {
// Add it to the queue if it hasn't already been put there.
if (!LazilyEmittedGlobalVariables.insert(v).second)
return;
assert(!FinishedEmittingLazyDefinitions);
LazyGlobalVariables.push_back(v);
if (auto decl = v->getDecl()) {
if (decl->getDeclContext()->getParentSourceFile())
return;
}
if (CurrentIGM == nullptr)
return;
// Don't update the map if we already have an entry.
DefaultIGMForGlobalVariable.insert(std::make_pair(v, CurrentIGM));
}
bool IRGenerator::hasLazyMetadata(TypeDecl *type) {
assert(isa<NominalTypeDecl>(type) ||
isa<OpaqueTypeDecl>(type));
@@ -2714,6 +2765,9 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
auto alignment =
Alignment(getClangASTContext().getDeclAlign(clangDecl).getQuantity());
return Address(addr, ti.getStorageType(), alignment);
} else if (!forDefinition &&
isLazilyEmittedGlobalVariable(*var, getSILModule())) {
IRGen.addLazyGlobalVariable(var);
}
ResilienceExpansion expansion = getResilienceExpansionForLayout(var);

View File

@@ -2300,6 +2300,22 @@ IRGenModule *IRGenerator::getGenModule(SILFunction *f) {
return getPrimaryIGM();
}
IRGenModule *IRGenerator::getGenModule(SILGlobalVariable *v) {
if (GenModules.size() == 1) {
return getPrimaryIGM();
}
auto found = DefaultIGMForGlobalVariable.find(v);
if (found != DefaultIGMForGlobalVariable.end())
return found->second;
if (auto decl = v->getDecl()) {
return getGenModule(decl->getDeclContext());
}
return getPrimaryIGM();
}
uint32_t swift::irgen::getSwiftABIVersion() {
return IRGenModule::swiftVersion;
}

View File

@@ -228,6 +228,10 @@ private:
// It is used if a function has no source-file association.
llvm::DenseMap<SILFunction *, IRGenModule *> DefaultIGMForFunction;
// Stores the IGM from which a global variable is referenced the first time.
// It is used if a global variable has no source-file association.
llvm::DenseMap<SILGlobalVariable *, IRGenModule *> DefaultIGMForGlobalVariable;
// The IGMs where specializations of functions are emitted. The key is the
// non-specialized function.
// Storing all specializations of a function in the same IGM increases the
@@ -327,6 +331,13 @@ private:
/// The queue of SIL functions to emit.
llvm::SmallVector<SILFunction *, 4> LazyFunctionDefinitions;
/// SIL global variables that have already been lazily emitted, or are
/// queued up.
llvm::SmallPtrSet<SILGlobalVariable *, 4> LazilyEmittedGlobalVariables;
/// The queue of SIL global variables to emit.
llvm::SmallVector<SILGlobalVariable *, 4> LazyGlobalVariables;
/// Witness tables that have already been lazily emitted, or are queued up.
llvm::SmallPtrSet<SILWitnessTable *, 4> LazilyEmittedWitnessTables;
@@ -388,6 +399,12 @@ public:
/// first time.
IRGenModule *getGenModule(SILFunction *f);
/// Get an IRGenModule for a global variable.
/// Returns the IRGenModule of the containing source file, or if this cannot
/// be determined, returns the IGM from which the global variable is
/// referenced the first time.
IRGenModule *getGenModule(SILGlobalVariable *v);
/// Returns the primary IRGenModule. This is the first added IRGenModule.
/// It is used for everything which cannot be correlated to a specific source
/// file. And of course, in single-threaded compilation there is only the
@@ -454,6 +471,8 @@ public:
void addLazyFunction(SILFunction *f);
void addLazyGlobalVariable(SILGlobalVariable *v);
void addDynamicReplacement(SILFunction *f) { DynamicReplacements.insert(f); }
void forceLocalEmitOfLazyFunction(SILFunction *f) {

View File

@@ -84,6 +84,9 @@ bool SILGlobalVariable::isPossiblyUsedExternally() const {
if (shouldBePreservedForDebugger())
return true;
if (markedAsUsed())
return true;
SILLinkage linkage = getLinkage();
return swift::isPossiblyUsedExternally(linkage, getModule().isWholeModule());
}

View File

@@ -11,7 +11,7 @@ struct T {
}
// CHECK: @global = hidden global %T4main1TV <{ i1 false, [3 x i8] undef, i32 0 }>, align 4
sil_global hidden @global : $T = {
sil_global hidden [used] @global : $T = {
%2 = integer_literal $Builtin.Int1, 0
%3 = integer_literal $Builtin.Int32, 0
%initval = struct $T (%2 : $Builtin.Int1, %3 : $Builtin.Int32)

View File

@@ -39,14 +39,6 @@ public struct LargeResilientStruct {
// CHECK: @largeGlobal = {{(dllexport )?}}{{(protected )?}}global [[BUFFER]] zeroinitializer
sil_global [let] @largeGlobal : $LargeResilientStruct
//
// Size is known in this resilience domain, and global is hidden,
// so allocate it directly.
//
// CHECK: @fixedGlobal = hidden global %T17global_resilience20LargeResilientStructV zeroinitializer
sil_global hidden @fixedGlobal : $LargeResilientStruct
//
// Unknown size -- must call value witness functions for buffer
// management.
@@ -55,6 +47,14 @@ sil_global hidden @fixedGlobal : $LargeResilientStruct
// CHECK: @otherGlobal = {{(dllexport )?}}{{(protected )?}}global [[BUFFER]] zeroinitializer
sil_global [let] @otherGlobal : $Size
//
// Size is known in this resilience domain, and global is hidden,
// so allocate it directly.
//
// CHECK: @fixedGlobal = hidden global %T17global_resilience20LargeResilientStructV zeroinitializer
sil_global hidden @fixedGlobal : $LargeResilientStruct
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @testSmallGlobal()
sil @testSmallGlobal : $@convention(thin) () -> Int32 {
bb0:

View File

@@ -21,9 +21,10 @@
//--- Library.swift
// TODO: Once global variables can be emitted lazily, these should be -NOT
// again, then show up in the application binary if we use them.
// LIBRARY-IR: @"$es23_swiftEmptyArrayStorageSi_S3itvp" = weak_odr {{(protected |dllexport )?}}global
// Never referenced.
// LIBRARY-IR-NOT: @"$es23_swiftEmptyArrayStorageSi_S3itvp" = weak_odr {{(protected |dllexport )?}}global
// Note: referenced by swift_allocEmptyBox.
// LIBRARY-IR: @"$es16_emptyBoxStorageSi_Sitvp" = weak_odr {{(protected |dllexport )?}}global
// LIBRARY-IR-NOT: define {{.*}}@"$e7Library5helloSaySiGyF"()

View File

@@ -33,4 +33,4 @@ public func main() {
// CHECK: @"$e4Main022global_in_client_used_c1_D0Sivp" = {{.*}}global %TSi zeroinitializer
// CHECK: @"$e4Main024global_in_client_unused_c1_D0Sivp" = {{.*}}global %TSi zeroinitializer
// CHECK: @"$e8MyModule022global_in_module_used_d1_E0Sivp" = {{.*}}global %TSi zeroinitializer
// CHECK: @"$e8MyModule024global_in_module_unused_d1_E0Sivp" = {{.*}}global %TSi zeroinitializer
// CHECK-NOT: @"$e8MyModule024global_in_module_unused_d1_E0Sivp" = {{.*}}global %TSi zeroinitializer