[CIR] Defer declarations and tentative definitions (#141700)

This change adds code to defer emitting declarations and tentative
definitions until they are referenced or trigger by a call to
CompleteTentativeDefinition. This is needed to avoid premature handling
of declarations and definitions that might not be referenced in the
current translation unit. It also avoids incorrectly adding an
initializer to external declarations.

This change also updates the way the insertion location for globals is
chosen so that all globals will be emitted together at the top of the
module. This makes no functional difference, but it is very useful for
writing sensible tests.

Some tests are modified in this change to reorder global variables so
that they can be checked in the order in which they will be emitted.
This commit is contained in:
Andy Kaylor
2025-05-28 13:53:30 -07:00
committed by GitHub
parent 711a1779dc
commit b574c811e8
13 changed files with 155 additions and 95 deletions

View File

@@ -54,6 +54,8 @@ public:
~CIRGenerator() override;
void Initialize(clang::ASTContext &astContext) override;
bool HandleTopLevelDecl(clang::DeclGroupRef group) override;
void CompleteTentativeDefinition(clang::VarDecl *d) override;
mlir::ModuleOp getModule() const;
mlir::MLIRContext &getMLIRContext() { return *mlirContext; };
const mlir::MLIRContext &getMLIRContext() const { return *mlirContext; };

View File

@@ -201,6 +201,7 @@ struct MissingFeatures {
static bool writebacks() { return false; }
static bool cleanupsToDeactivate() { return false; }
static bool stackBase() { return false; }
static bool deferredDecls() { return false; }
// Missing types
static bool dataMemberType() { return false; }

View File

@@ -231,8 +231,20 @@ void CIRGenModule::emitGlobal(clang::GlobalDecl gd) {
return;
}
} else {
assert(cast<VarDecl>(global)->isFileVarDecl() &&
"Cannot emit local var decl as global");
const auto *vd = cast<VarDecl>(global);
assert(vd->isFileVarDecl() && "Cannot emit local var decl as global.");
if (vd->isThisDeclarationADefinition() != VarDecl::Definition &&
!astContext.isMSStaticDataMemberInlineDefinition(vd)) {
assert(!cir::MissingFeatures::openMP());
// If this declaration may have caused an inline variable definition to
// change linkage, make sure that it's emitted.
if (astContext.getInlineVariableDefinitionKind(vd) ==
ASTContext::InlineVariableDefinitionKind::Strong)
getAddrOfGlobalVar(vd);
// Otherwise, we can ignore this declaration. The variable will be emitted
// on its first use.
return;
}
}
// TODO(CIR): Defer emitting some global definitions until later
@@ -279,21 +291,22 @@ cir::GlobalOp CIRGenModule::createGlobalOp(CIRGenModule &cgm,
{
mlir::OpBuilder::InsertionGuard guard(builder);
// Some global emissions are triggered while emitting a function, e.g.
// void s() { const char *s = "yolo"; ... }
//
// Be sure to insert global before the current function
CIRGenFunction *curCGF = cgm.curCGF;
if (curCGF)
builder.setInsertionPoint(curCGF->curFn);
// If an insertion point is provided, we're replacing an existing global,
// otherwise, create the new global immediately after the last gloabl we
// emitted.
if (insertPoint) {
builder.setInsertionPoint(insertPoint);
} else {
// Group global operations together at the top of the module.
if (cgm.lastGlobalOp)
builder.setInsertionPointAfter(cgm.lastGlobalOp);
else
builder.setInsertionPointToStart(cgm.getModule().getBody());
}
g = builder.create<cir::GlobalOp>(loc, name, t);
if (!curCGF) {
if (insertPoint)
cgm.getModule().insert(insertPoint, g);
else
cgm.getModule().push_back(g);
}
if (!insertPoint)
cgm.lastGlobalOp = g;
// Default to private until we can judge based on the initializer,
// since MLIR doesn't allow public declarations.
@@ -1044,6 +1057,24 @@ StringRef CIRGenModule::getMangledName(GlobalDecl gd) {
return mangledDeclNames[canonicalGd] = result.first->first();
}
void CIRGenModule::emitTentativeDefinition(const VarDecl *d) {
assert(!d->getInit() && "Cannot emit definite definitions here!");
StringRef mangledName = getMangledName(d);
mlir::Operation *gv = getGlobalValue(mangledName);
// If we already have a definition, not declaration, with the same mangled
// name, emitting of declaration is not required (and would actually overwrite
// the emitted definition).
if (gv && !mlir::cast<cir::GlobalOp>(gv).isDeclaration())
return;
assert(!cir::MissingFeatures::deferredDecls());
// The tentative definition is the only definition.
emitGlobalVarDefinition(d);
}
cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
StringRef mangledName, mlir::Type funcType, GlobalDecl gd, bool forVTable,
bool dontDefer, bool isThunk, ForDefinition_t isForDefinition,

View File

@@ -111,6 +111,8 @@ public:
/// Handling globals
/// -------
mlir::Operation *lastGlobalOp = nullptr;
mlir::Operation *getGlobalValue(llvm::StringRef ref);
/// If the specified mangled name is not in the module, create and return an
@@ -194,6 +196,8 @@ public:
llvm::StringRef getMangledName(clang::GlobalDecl gd);
void emitTentativeDefinition(const VarDecl *d);
static void setInitializer(cir::GlobalOp &op, mlir::Attribute value);
cir::FuncOp

View File

@@ -62,3 +62,10 @@ bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) {
return true;
}
void CIRGenerator::CompleteTentativeDefinition(VarDecl *d) {
if (diags.hasErrorOccurred())
return;
cgm->emitTentativeDefinition(d);
}

View File

@@ -135,6 +135,10 @@ public:
}
}
}
void CompleteTentativeDefinition(VarDecl *D) override {
Gen->CompleteTentativeDefinition(D);
}
};
} // namespace cir

View File

@@ -19,16 +19,6 @@ int aa[10][5];
// OGCG: @aa = global [10 x [5 x i32]] zeroinitializer
extern int b[10];
// CIR: cir.global external @b = #cir.zero : !cir.array<!s32i x 10>
// LLVM: @b = dso_local global [10 x i32] zeroinitializer
extern int bb[10][5];
// CIR: cir.global external @bb = #cir.zero : !cir.array<!cir.array<!s32i x 5> x 10>
// LLVM: @bb = dso_local global [10 x [5 x i32]] zeroinitializer
int c[10] = {};
// CIR: cir.global external @c = #cir.zero : !cir.array<!s32i x 10>
@@ -66,6 +56,22 @@ int f[5] = {1, 2};
// OGCG: @f = global [5 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0]
extern int b[10];
// CIR: cir.global external @b : !cir.array<!s32i x 10>
// LLVM: @b = external dso_local global [10 x i32]
// OGCG: @b = external global [10 x i32]
extern int bb[10][5];
// CIR: cir.global external @bb : !cir.array<!cir.array<!s32i x 5> x 10>
// LLVM: @bb = external dso_local global [10 x [5 x i32]]
// OGCG: @bb = external global [10 x [5 x i32]]
// This function is only here to make sure the external globals are emitted.
void reference_externs() {
b;
bb;
}
// OGCG: @[[FUN2_ARR:.*]] = private unnamed_addr constant [2 x i32] [i32 5, i32 0], align 4
// OGCG: @[[FUN3_ARR:.*]] = private unnamed_addr constant [2 x i32] [i32 5, i32 6], align 4
// OGCG: @[[FUN4_ARR:.*]] = private unnamed_addr constant [2 x [1 x i32]] [

View File

@@ -5,6 +5,28 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
enum A {
A_one,
A_two
};
enum A a;
// CHECK: cir.global external @a = #cir.int<0> : !u32i
enum B : int;
enum B b;
// CHECK: cir.global external @b = #cir.int<0> : !u32i
enum C : int {
C_one,
C_two
};
enum C c;
// CHECK: cir.global external @c = #cir.int<0> : !u32i
int f1(int i);
int f1(int i) {
@@ -12,8 +34,7 @@ int f1(int i) {
return i;
}
// CIR: module
// CIR-NEXT: cir.func @f1(%arg0: !s32i loc({{.*}})) -> !s32i
// CIR: cir.func @f1(%arg0: !s32i loc({{.*}})) -> !s32i
// CIR-NEXT: %[[I_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i", init] {alignment = 4 : i64}
// CIR-NEXT: %[[RV:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
// CIR-NEXT: cir.store{{.*}} %arg0, %[[I_PTR]] : !s32i, !cir.ptr<!s32i>
@@ -288,25 +309,3 @@ size_type max_size(void) {
// CHECK: %6 = cir.load{{.*}} %0 : !cir.ptr<!u64i>, !u64i
// CHECK: cir.return %6 : !u64i
// CHECK: }
enum A {
A_one,
A_two
};
enum A a;
// CHECK: cir.global external @a = #cir.int<0> : !u32i
enum B : int;
enum B b;
// CHECK: cir.global external @b = #cir.int<0> : !u32i
enum C : int {
C_one,
C_two
};
enum C c;
// CHECK: cir.global external @c = #cir.int<0> : !u32i

View File

@@ -1,11 +1,36 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s
enum A {
A_one,
A_two
};
enum A a;
// CHECK: cir.global external @a = #cir.int<0> : !u32i
enum B : int;
enum B b;
// CHECK: cir.global external @b = #cir.int<0> : !s32i
enum C : int {
C_one,
C_two
};
enum C c;
// CHECK: cir.global external @c = #cir.int<0> : !s32i
enum class D : int;
enum D d;
// CHECK: cir.global external @d = #cir.int<0> : !s32i
int f1() {
int i;
return i;
}
// CHECK: module
// CHECK: cir.func @_Z2f1v() -> !s32i
// CHECK: %[[RV:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
// CHECK: %[[I_PTR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["i"] {alignment = 4 : i64}
@@ -145,29 +170,3 @@ void ref_local(short x) {
// CHECK: %[[Y_REF_ADDR:.*]] = cir.alloca !cir.ptr<!s16i>, !cir.ptr<!cir.ptr<!s16i>>, ["y", init, const] {alignment = 8 : i64}
// CHECK: cir.store{{.*}} %[[ARG]], %[[X_ADDR]] : !s16i, !cir.ptr<!s16i>
// CHECK: cir.store{{.*}} %[[X_ADDR]], %[[Y_REF_ADDR]] : !cir.ptr<!s16i>, !cir.ptr<!cir.ptr<!s16i>>
enum A {
A_one,
A_two
};
enum A a;
// CHECK: cir.global external @a = #cir.int<0> : !u32i
enum B : int;
enum B b;
// CHECK: cir.global external @b = #cir.int<0> : !s32i
enum C : int {
C_one,
C_two
};
enum C c;
// CHECK: cir.global external @c = #cir.int<0> : !s32i
enum class D : int;
enum D d;
// CHECK: cir.global external @d = #cir.int<0> : !s32i

View File

@@ -5,6 +5,10 @@
// RUN: %clang_cc1 -triple aarch64-none-linux-android21 -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
// CIR: cir.global external @[[STR1_GLOBAL:.*]] = #cir.const_array<"1\00" : !cir.array<!s8i x 2>> : !cir.array<!s8i x 2>
// CIR: cir.global external @[[STR2_GLOBAL:.*]] = #cir.zero : !cir.array<!s8i x 1>
// CIR: cir.global external @[[STR3_GLOBAL:.*]] = #cir.zero : !cir.array<!s8i x 2>
// LLVM: @[[STR1_GLOBAL:.*]] = dso_local global [2 x i8] c"1\00"
// LLVM: @[[STR2_GLOBAL:.*]] = dso_local global [1 x i8] zeroinitializer
// LLVM: @[[STR3_GLOBAL:.*]] = dso_local global [2 x i8] zeroinitializer
@@ -17,7 +21,6 @@ char *f1() {
return "1";
}
// CIR: cir.global external @[[STR1_GLOBAL:.*]] = #cir.const_array<"1\00" : !cir.array<!s8i x 2>> : !cir.array<!s8i x 2>
// CIR: cir.func @f1()
// CIR: %[[STR:.*]] = cir.get_global @[[STR1_GLOBAL]] : !cir.ptr<!cir.array<!s8i x 2>>
@@ -31,7 +34,6 @@ char *f2() {
return "";
}
// CIR: cir.global external @[[STR2_GLOBAL:.*]] = #cir.zero : !cir.array<!s8i x 1>
// CIR: cir.func @f2()
// CIR: %[[STR2:.*]] = cir.get_global @[[STR2_GLOBAL]] : !cir.ptr<!cir.array<!s8i x 1>>
@@ -45,7 +47,6 @@ char *f3() {
return "\00";
}
// CIR: cir.global external @[[STR3_GLOBAL:.*]] = #cir.zero : !cir.array<!s8i x 2>
// CIR: cir.func @f3()
// CIR: %[[STR3:.*]] = cir.get_global @[[STR3_GLOBAL]] : !cir.ptr<!cir.array<!s8i x 2>>

View File

@@ -42,12 +42,6 @@
// OGCG-DAG: %struct.CycleMiddle = type { ptr }
// OGCG-DAG: %struct.CycleEnd = type { ptr }
struct IncompleteS *p;
// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!rec_IncompleteS>
// LLVM-DAG: @p = dso_local global ptr null
// OGCG-DAG: @p = global ptr null, align 8
struct CompleteS {
int a;
char b;
@@ -57,6 +51,12 @@ struct CompleteS {
// LLVM-DAG: @cs = dso_local global %struct.CompleteS zeroinitializer
// OGCG-DAG: @cs = global %struct.CompleteS zeroinitializer, align 4
struct IncompleteS *p;
// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!rec_IncompleteS>
// LLVM-DAG: @p = dso_local global ptr null
// OGCG-DAG: @p = global ptr null, align 8
struct InnerS {
int a;
char b;

View File

@@ -7,12 +7,6 @@ int a[10];
int aa[10][5];
// CHECK: @aa = dso_local global [10 x [5 x i32]] zeroinitializer
extern int b[10];
// CHECK: @b = dso_local global [10 x i32] zeroinitializer
extern int bb[10][5];
// CHECK: @bb = dso_local global [10 x [5 x i32]] zeroinitializer
int c[10] = {};
// CHECK: @c = dso_local global [10 x i32] zeroinitializer
@@ -30,6 +24,18 @@ int e[10] = {1, 2};
int f[5] = {1, 2};
// CHECK: @f = dso_local global [5 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0]
extern int b[10];
// CHECK: @b = external dso_local global [10 x i32]
extern int bb[10][5];
// CHECK: @bb = external dso_local global [10 x [5 x i32]]
// This function is only here to make sure the external globals are emitted.
void reference_externs() {
b;
bb;
}
void func() {
int arr[10];
int e = arr[0];

View File

@@ -1,10 +1,10 @@
// Smoke test for ClangIR-to-LLVM IR code generation
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s
int a;
// CHECK: @a = dso_local global i32 0
int b = 2;
// CHECK: @b = dso_local global i32 2
int a;
// CHECK: @a = dso_local global i32 0