Skip to content

Commit ab15602

Browse files
authored
Merge pull request #84110 from DougGregor/lazy-irgen-global-variables
[IRGen] Lazily emit SIL global variables
2 parents f24f5de + dbc30dc commit ab15602

File tree

9 files changed

+125
-27
lines changed

9 files changed

+125
-27
lines changed

lib/IRGen/GenDecl.cpp

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,21 @@ static bool isLazilyEmittedFunction(SILFunction &f, SILModule &m) {
11741174
return true;
11751175
}
11761176

1177+
// Eagerly emit global variables that are externally visible.
1178+
static bool isLazilyEmittedGlobalVariable(SILGlobalVariable &v, SILModule &m) {
1179+
if (v.isPossiblyUsedExternally()) {
1180+
// Under the embedded linkage model, if it has a non-unique definition,
1181+
// treat it lazily.
1182+
if (v.hasNonUniqueDefinition() && !v.markedAsUsed()) {
1183+
return true;
1184+
}
1185+
1186+
return false;
1187+
}
1188+
1189+
return true;
1190+
}
1191+
11771192
void IRGenerator::emitGlobalTopLevel(
11781193
const std::vector<std::string> &linkerDirectives) {
11791194
if (PrimaryIGM->getSILModule().getOptions().StopOptimizationAfterSerialization) {
@@ -1207,6 +1222,9 @@ void IRGenerator::emitGlobalTopLevel(
12071222
createLinkerDirectiveVariable(*PrimaryIGM, directive);
12081223
}
12091224
for (SILGlobalVariable &v : PrimaryIGM->getSILModule().getSILGlobals()) {
1225+
if (isLazilyEmittedGlobalVariable(v, PrimaryIGM->getSILModule()))
1226+
continue;
1227+
12101228
Decl *decl = v.getDecl();
12111229
CurrentIGMPtr IGM = getGenModule(decl ? decl->getDeclContext() : nullptr);
12121230
IGM->emitSILGlobalVariable(&v);
@@ -1368,6 +1386,7 @@ void IRGenerator::emitLazyDefinitions() {
13681386
!LazyExtensionDescriptors.empty() ||
13691387
!LazyFieldDescriptors.empty() ||
13701388
!LazyFunctionDefinitions.empty() || !LazyWitnessTables.empty() ||
1389+
!LazyGlobalVariables.empty() ||
13711390
!LazyCanonicalSpecializedMetadataAccessors.empty() ||
13721391
!LazyMetadataAccessors.empty() ||
13731392
!LazyClassMetadata.empty() ||
@@ -1456,6 +1475,13 @@ void IRGenerator::emitLazyDefinitions() {
14561475
IGM->emitSILFunction(f);
14571476
}
14581477

1478+
// Emit any lazy global variables we require.
1479+
while (!LazyGlobalVariables.empty()) {
1480+
SILGlobalVariable *v = LazyGlobalVariables.pop_back_val();
1481+
CurrentIGMPtr IGM = getGenModule(v);
1482+
IGM->emitSILGlobalVariable(v);
1483+
}
1484+
14591485
while (!LazyCanonicalSpecializedMetadataAccessors.empty()) {
14601486
CanType theType =
14611487
LazyCanonicalSpecializedMetadataAccessors.pop_back_val();
@@ -1529,6 +1555,26 @@ void IRGenerator::addLazyFunction(SILFunction *f) {
15291555
DefaultIGMForFunction.insert(std::make_pair(f, CurrentIGM));
15301556
}
15311557

1558+
void IRGenerator::addLazyGlobalVariable(SILGlobalVariable *v) {
1559+
// Add it to the queue if it hasn't already been put there.
1560+
if (!LazilyEmittedGlobalVariables.insert(v).second)
1561+
return;
1562+
1563+
assert(!FinishedEmittingLazyDefinitions);
1564+
LazyGlobalVariables.push_back(v);
1565+
1566+
if (auto decl = v->getDecl()) {
1567+
if (decl->getDeclContext()->getParentSourceFile())
1568+
return;
1569+
}
1570+
1571+
if (CurrentIGM == nullptr)
1572+
return;
1573+
1574+
// Don't update the map if we already have an entry.
1575+
DefaultIGMForGlobalVariable.insert(std::make_pair(v, CurrentIGM));
1576+
}
1577+
15321578
bool IRGenerator::hasLazyMetadata(TypeDecl *type) {
15331579
assert(isa<NominalTypeDecl>(type) ||
15341580
isa<OpaqueTypeDecl>(type));
@@ -2714,6 +2760,9 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
27142760
auto alignment =
27152761
Alignment(getClangASTContext().getDeclAlign(clangDecl).getQuantity());
27162762
return Address(addr, ti.getStorageType(), alignment);
2763+
} else if (!forDefinition &&
2764+
isLazilyEmittedGlobalVariable(*var, getSILModule())) {
2765+
IRGen.addLazyGlobalVariable(var);
27172766
}
27182767

27192768
ResilienceExpansion expansion = getResilienceExpansionForLayout(var);
@@ -2726,7 +2775,7 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
27262775

27272776
if (var->isInitializedObject()) {
27282777
assert(ti.isFixedSize(expansion));
2729-
StructLayout *Layout = StaticObjectLayouts[var].get();
2778+
StructLayout *Layout = StaticObjectLayouts[var].layout.get();
27302779
if (!Layout) {
27312780
// Create the layout (includes the llvm type) for the statically
27322781
// initialized object and store it for later.
@@ -2737,7 +2786,7 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
27372786
}
27382787
Layout = getClassLayoutWithTailElems(*this,
27392788
var->getLoweredType(), TailTypes);
2740-
StaticObjectLayouts[var] = std::unique_ptr<StructLayout>(Layout);
2789+
StaticObjectLayouts[var] = {std::unique_ptr<StructLayout>(Layout), nullptr};
27412790
}
27422791
storageType = Layout->getType();
27432792
fixedSize = Layout->getSize();
@@ -2853,7 +2902,7 @@ llvm::Constant *IRGenModule::getGlobalInitValue(SILGlobalVariable *var,
28532902
llvm::Type *storageType,
28542903
Alignment alignment) {
28552904
if (var->isInitializedObject()) {
2856-
StructLayout *layout = StaticObjectLayouts[var].get();
2905+
StructLayout *layout = StaticObjectLayouts[var].layout.get();
28572906
ObjectInst *oi = cast<ObjectInst>(var->getStaticInitializerValue());
28582907
llvm::Constant *initVal = emitConstantObject(*this, oi, layout);
28592908
if (!canMakeStaticObjectReadOnly(var->getLoweredType())) {
@@ -2863,13 +2912,18 @@ llvm::Constant *IRGenModule::getGlobalInitValue(SILGlobalVariable *var,
28632912
// swift_once_t token[fixedAlignment / sizeof(swift_once_t)];
28642913
// HeapObject object;
28652914
// };
2866-
std::string typeName = storageType->getStructName().str() + 'c';
2867-
assert(alignment >= getPointerAlignment());
2868-
unsigned numTokens = alignment.getValue() /
2869-
getPointerAlignment().getValue();
2870-
auto *containerTy = llvm::StructType::create(getLLVMContext(),
2871-
{llvm::ArrayType::get(OnceTy, numTokens), initVal->getType()},
2872-
typeName);
2915+
llvm::StructType *containerTy = StaticObjectLayouts[var].containerTy;
2916+
if (!containerTy) {
2917+
std::string typeName = storageType->getStructName().str() + 'c';
2918+
assert(alignment >= getPointerAlignment());
2919+
unsigned numTokens = alignment.getValue() /
2920+
getPointerAlignment().getValue();
2921+
containerTy = llvm::StructType::create(getLLVMContext(),
2922+
{llvm::ArrayType::get(OnceTy, numTokens), initVal->getType()},
2923+
typeName);
2924+
StaticObjectLayouts[var].containerTy = containerTy;
2925+
}
2926+
28732927
auto *zero = llvm::ConstantAggregateZero::get(containerTy->getElementType(0));
28742928
initVal = llvm::ConstantStruct::get(containerTy, {zero , initVal});
28752929
}

lib/IRGen/IRGenModule.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,6 +2300,22 @@ IRGenModule *IRGenerator::getGenModule(SILFunction *f) {
23002300
return getPrimaryIGM();
23012301
}
23022302

2303+
IRGenModule *IRGenerator::getGenModule(SILGlobalVariable *v) {
2304+
if (GenModules.size() == 1) {
2305+
return getPrimaryIGM();
2306+
}
2307+
2308+
auto found = DefaultIGMForGlobalVariable.find(v);
2309+
if (found != DefaultIGMForGlobalVariable.end())
2310+
return found->second;
2311+
2312+
if (auto decl = v->getDecl()) {
2313+
return getGenModule(decl->getDeclContext());
2314+
}
2315+
2316+
return getPrimaryIGM();
2317+
}
2318+
23032319
uint32_t swift::irgen::getSwiftABIVersion() {
23042320
return IRGenModule::swiftVersion;
23052321
}

lib/IRGen/IRGenModule.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ class IRGenerator {
228228
// It is used if a function has no source-file association.
229229
llvm::DenseMap<SILFunction *, IRGenModule *> DefaultIGMForFunction;
230230

231+
// Stores the IGM from which a global variable is referenced the first time.
232+
// It is used if a global variable has no source-file association.
233+
llvm::DenseMap<SILGlobalVariable *, IRGenModule *> DefaultIGMForGlobalVariable;
234+
231235
// The IGMs where specializations of functions are emitted. The key is the
232236
// non-specialized function.
233237
// Storing all specializations of a function in the same IGM increases the
@@ -327,6 +331,13 @@ class IRGenerator {
327331
/// The queue of SIL functions to emit.
328332
llvm::SmallVector<SILFunction *, 4> LazyFunctionDefinitions;
329333

334+
/// SIL global variables that have already been lazily emitted, or are
335+
/// queued up.
336+
llvm::SmallPtrSet<SILGlobalVariable *, 4> LazilyEmittedGlobalVariables;
337+
338+
/// The queue of SIL global variables to emit.
339+
llvm::SmallVector<SILGlobalVariable *, 4> LazyGlobalVariables;
340+
330341
/// Witness tables that have already been lazily emitted, or are queued up.
331342
llvm::SmallPtrSet<SILWitnessTable *, 4> LazilyEmittedWitnessTables;
332343

@@ -388,6 +399,12 @@ class IRGenerator {
388399
/// first time.
389400
IRGenModule *getGenModule(SILFunction *f);
390401

402+
/// Get an IRGenModule for a global variable.
403+
/// Returns the IRGenModule of the containing source file, or if this cannot
404+
/// be determined, returns the IGM from which the global variable is
405+
/// referenced the first time.
406+
IRGenModule *getGenModule(SILGlobalVariable *v);
407+
391408
/// Returns the primary IRGenModule. This is the first added IRGenModule.
392409
/// It is used for everything which cannot be correlated to a specific source
393410
/// file. And of course, in single-threaded compilation there is only the
@@ -454,6 +471,8 @@ class IRGenerator {
454471

455472
void addLazyFunction(SILFunction *f);
456473

474+
void addLazyGlobalVariable(SILGlobalVariable *v);
475+
457476
void addDynamicReplacement(SILFunction *f) { DynamicReplacements.insert(f); }
458477

459478
void forceLocalEmitOfLazyFunction(SILFunction *f) {
@@ -1407,8 +1426,14 @@ class IRGenModule {
14071426
friend struct ::llvm::DenseMapInfo<swift::irgen::IRGenModule::FixedLayoutKey>;
14081427
llvm::DenseMap<FixedLayoutKey, llvm::Constant *> PrivateFixedLayouts;
14091428

1429+
/// Captures a static object layout.
1430+
struct StaticObjectLayout {
1431+
std::unique_ptr<StructLayout> layout;
1432+
llvm::StructType *containerTy = nullptr;
1433+
};
1434+
14101435
/// A cache for layouts of statically initialized objects.
1411-
llvm::DenseMap<SILGlobalVariable *, std::unique_ptr<StructLayout>>
1436+
llvm::DenseMap<SILGlobalVariable *, StaticObjectLayout>
14121437
StaticObjectLayouts;
14131438

14141439
/// A mapping from order numbers to the LLVM functions which we

lib/SIL/IR/SILGlobalVariable.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ bool SILGlobalVariable::isPossiblyUsedExternally() const {
8484
if (shouldBePreservedForDebugger())
8585
return true;
8686

87+
if (markedAsUsed())
88+
return true;
89+
8790
SILLinkage linkage = getLinkage();
8891
return swift::isPossiblyUsedExternally(linkage, getModule().isWholeModule());
8992
}

test/IRGen/constant_struct_with_padding.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct T {
1111
}
1212

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

test/IRGen/global_resilience.sil

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,6 @@ public struct LargeResilientStruct {
3939
// CHECK: @largeGlobal = {{(dllexport )?}}{{(protected )?}}global [[BUFFER]] zeroinitializer
4040
sil_global [let] @largeGlobal : $LargeResilientStruct
4141

42-
//
43-
// Size is known in this resilience domain, and global is hidden,
44-
// so allocate it directly.
45-
//
46-
47-
// CHECK: @fixedGlobal = hidden global %T17global_resilience20LargeResilientStructV zeroinitializer
48-
sil_global hidden @fixedGlobal : $LargeResilientStruct
49-
5042
//
5143
// Unknown size -- must call value witness functions for buffer
5244
// management.
@@ -55,6 +47,14 @@ sil_global hidden @fixedGlobal : $LargeResilientStruct
5547
// CHECK: @otherGlobal = {{(dllexport )?}}{{(protected )?}}global [[BUFFER]] zeroinitializer
5648
sil_global [let] @otherGlobal : $Size
5749

50+
//
51+
// Size is known in this resilience domain, and global is hidden,
52+
// so allocate it directly.
53+
//
54+
55+
// CHECK: @fixedGlobal = hidden global %T17global_resilience20LargeResilientStructV zeroinitializer
56+
sil_global hidden @fixedGlobal : $LargeResilientStruct
57+
5858
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @testSmallGlobal()
5959
sil @testSmallGlobal : $@convention(thin) () -> Int32 {
6060
bb0:

test/IRGen/lazy_globals.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// RUN: %target-swift-frontend -parse-as-library -emit-ir -primary-file %s | %FileCheck %s
22

3-
// REQUIRES: CPU=x86_64
3+
// REQUIRES: CPU=x86_64 || CPU=arm64
44

5-
// CHECK: @"[[T:.*]]Wz" = internal global i64 0, align 8
65
// CHECK: @"$s12lazy_globals1xSivp" = hidden global %TSi zeroinitializer, align 8
76
// CHECK: @"$s12lazy_globals1ySivp" = hidden global %TSi zeroinitializer, align 8
87
// CHECK: @"$s12lazy_globals1zSivp" = hidden global %TSi zeroinitializer, align 8
8+
// CHECK: @"[[T:.*]]Wz" = internal global i64 0, align 8
99

1010
// CHECK: define internal void @"[[T]]WZ"(ptr %0) {{.*}} {
1111
// CHECK: entry:
@@ -39,4 +39,3 @@ var (x, y, z) = (1, 2, 3)
3939
// CHECK: ret i64 [[V]]
4040
// CHECK: }
4141
func getX() -> Int { return x }
42-

test/embedded/linkage/leaf_application.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121

2222
//--- Library.swift
2323

24-
// TODO: Once global variables can be emitted lazily, these should be -NOT
25-
// again, then show up in the application binary if we use them.
26-
// LIBRARY-IR: @"$es23_swiftEmptyArrayStorageSi_S3itvp" = weak_odr {{(protected |dllexport )?}}global
24+
// Never referenced.
25+
// LIBRARY-IR-NOT: @"$es23_swiftEmptyArrayStorageSi_S3itvp" = weak_odr {{(protected |dllexport )?}}global
26+
27+
// Note: referenced by swift_allocEmptyBox.
2728
// LIBRARY-IR: @"$es16_emptyBoxStorageSi_Sitvp" = weak_odr {{(protected |dllexport )?}}global
2829

2930
// LIBRARY-IR-NOT: define {{.*}}@"$e7Library5helloSaySiGyF"()

test/embedded/modules-globals.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ public func main() {
3333
// CHECK: @"$e4Main022global_in_client_used_c1_D0Sivp" = {{.*}}global %TSi zeroinitializer
3434
// CHECK: @"$e4Main024global_in_client_unused_c1_D0Sivp" = {{.*}}global %TSi zeroinitializer
3535
// CHECK: @"$e8MyModule022global_in_module_used_d1_E0Sivp" = {{.*}}global %TSi zeroinitializer
36-
// CHECK: @"$e8MyModule024global_in_module_unused_d1_E0Sivp" = {{.*}}global %TSi zeroinitializer
36+
// CHECK-NOT: @"$e8MyModule024global_in_module_unused_d1_E0Sivp" = {{.*}}global %TSi zeroinitializer

0 commit comments

Comments
 (0)