[IRGen] Cache the layout of the LLVM struct type for statically-initialized objects

Lazy emission of SIL global variables caused us to go through the
creation of two different LLVM struct types for the same
initialization, tripping an assertion in LLVM. Cache it along with
other information about the static-initialized object rather than
rebuilding it.

Also fix the lazy_globals test to account for the laziness,
generalizing it to also run on arm64 so I won't miss it locally.
This commit is contained in:
Doug Gregor
2025-09-04 15:58:32 -07:00
parent 4009814783
commit dbc30dc5c9
3 changed files with 24 additions and 19 deletions

View File

@@ -1176,11 +1176,6 @@ static bool isLazilyEmittedFunction(SILFunction &f, SILModule &m) {
// 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.
@@ -2780,7 +2775,7 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
if (var->isInitializedObject()) {
assert(ti.isFixedSize(expansion));
StructLayout *Layout = StaticObjectLayouts[var].get();
StructLayout *Layout = StaticObjectLayouts[var].layout.get();
if (!Layout) {
// Create the layout (includes the llvm type) for the statically
// initialized object and store it for later.
@@ -2791,7 +2786,7 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
}
Layout = getClassLayoutWithTailElems(*this,
var->getLoweredType(), TailTypes);
StaticObjectLayouts[var] = std::unique_ptr<StructLayout>(Layout);
StaticObjectLayouts[var] = {std::unique_ptr<StructLayout>(Layout), nullptr};
}
storageType = Layout->getType();
fixedSize = Layout->getSize();
@@ -2907,7 +2902,7 @@ llvm::Constant *IRGenModule::getGlobalInitValue(SILGlobalVariable *var,
llvm::Type *storageType,
Alignment alignment) {
if (var->isInitializedObject()) {
StructLayout *layout = StaticObjectLayouts[var].get();
StructLayout *layout = StaticObjectLayouts[var].layout.get();
ObjectInst *oi = cast<ObjectInst>(var->getStaticInitializerValue());
llvm::Constant *initVal = emitConstantObject(*this, oi, layout);
if (!canMakeStaticObjectReadOnly(var->getLoweredType())) {
@@ -2917,13 +2912,18 @@ llvm::Constant *IRGenModule::getGlobalInitValue(SILGlobalVariable *var,
// swift_once_t token[fixedAlignment / sizeof(swift_once_t)];
// HeapObject object;
// };
std::string typeName = storageType->getStructName().str() + 'c';
assert(alignment >= getPointerAlignment());
unsigned numTokens = alignment.getValue() /
getPointerAlignment().getValue();
auto *containerTy = llvm::StructType::create(getLLVMContext(),
{llvm::ArrayType::get(OnceTy, numTokens), initVal->getType()},
typeName);
llvm::StructType *containerTy = StaticObjectLayouts[var].containerTy;
if (!containerTy) {
std::string typeName = storageType->getStructName().str() + 'c';
assert(alignment >= getPointerAlignment());
unsigned numTokens = alignment.getValue() /
getPointerAlignment().getValue();
containerTy = llvm::StructType::create(getLLVMContext(),
{llvm::ArrayType::get(OnceTy, numTokens), initVal->getType()},
typeName);
StaticObjectLayouts[var].containerTy = containerTy;
}
auto *zero = llvm::ConstantAggregateZero::get(containerTy->getElementType(0));
initVal = llvm::ConstantStruct::get(containerTy, {zero , initVal});
}

View File

@@ -1426,8 +1426,14 @@ private:
friend struct ::llvm::DenseMapInfo<swift::irgen::IRGenModule::FixedLayoutKey>;
llvm::DenseMap<FixedLayoutKey, llvm::Constant *> PrivateFixedLayouts;
/// Captures a static object layout.
struct StaticObjectLayout {
std::unique_ptr<StructLayout> layout;
llvm::StructType *containerTy = nullptr;
};
/// A cache for layouts of statically initialized objects.
llvm::DenseMap<SILGlobalVariable *, std::unique_ptr<StructLayout>>
llvm::DenseMap<SILGlobalVariable *, StaticObjectLayout>
StaticObjectLayouts;
/// A mapping from order numbers to the LLVM functions which we

View File

@@ -1,11 +1,11 @@
// RUN: %target-swift-frontend -parse-as-library -emit-ir -primary-file %s | %FileCheck %s
// REQUIRES: CPU=x86_64
// REQUIRES: CPU=x86_64 || CPU=arm64
// CHECK: @"[[T:.*]]Wz" = internal global i64 0, align 8
// CHECK: @"$s12lazy_globals1xSivp" = hidden global %TSi zeroinitializer, align 8
// CHECK: @"$s12lazy_globals1ySivp" = hidden global %TSi zeroinitializer, align 8
// CHECK: @"$s12lazy_globals1zSivp" = hidden global %TSi zeroinitializer, align 8
// CHECK: @"[[T:.*]]Wz" = internal global i64 0, align 8
// CHECK: define internal void @"[[T]]WZ"(ptr %0) {{.*}} {
// CHECK: entry:
@@ -39,4 +39,3 @@ var (x, y, z) = (1, 2, 3)
// CHECK: ret i64 [[V]]
// CHECK: }
func getX() -> Int { return x }