From b574c811e83a2f638714153cb2f915c625ac945c Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Wed, 28 May 2025 13:53:30 -0700 Subject: [PATCH] [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. --- clang/include/clang/CIR/CIRGenerator.h | 2 + clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/CodeGen/CIRGenModule.cpp | 61 ++++++++++++++----- clang/lib/CIR/CodeGen/CIRGenModule.h | 4 ++ clang/lib/CIR/CodeGen/CIRGenerator.cpp | 7 +++ clang/lib/CIR/FrontendAction/CIRGenAction.cpp | 4 ++ clang/test/CIR/CodeGen/array.cpp | 26 +++++--- clang/test/CIR/CodeGen/basic.c | 47 +++++++------- clang/test/CIR/CodeGen/basic.cpp | 53 ++++++++-------- clang/test/CIR/CodeGen/string-literals.c | 7 ++- clang/test/CIR/CodeGen/struct.c | 12 ++-- clang/test/CIR/Lowering/array.cpp | 18 ++++-- clang/test/CIR/Lowering/hello.c | 8 +-- 13 files changed, 155 insertions(+), 95 deletions(-) diff --git a/clang/include/clang/CIR/CIRGenerator.h b/clang/include/clang/CIR/CIRGenerator.h index 883dce9deb8e..bb20fdf72693 100644 --- a/clang/include/clang/CIR/CIRGenerator.h +++ b/clang/include/clang/CIR/CIRGenerator.h @@ -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; }; diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index fb205e9cd85d..56bf9b1130f1 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -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; } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 60a3048f548e..b31b2b33d7d8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -231,8 +231,20 @@ void CIRGenModule::emitGlobal(clang::GlobalDecl gd) { return; } } else { - assert(cast(global)->isFileVarDecl() && - "Cannot emit local var decl as global"); + const auto *vd = cast(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(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(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, diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index add1b40ab0ae..aac8fe72b189 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -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 diff --git a/clang/lib/CIR/CodeGen/CIRGenerator.cpp b/clang/lib/CIR/CodeGen/CIRGenerator.cpp index 40252ffecfba..726da5b01326 100644 --- a/clang/lib/CIR/CodeGen/CIRGenerator.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenerator.cpp @@ -62,3 +62,10 @@ bool CIRGenerator::HandleTopLevelDecl(DeclGroupRef group) { return true; } + +void CIRGenerator::CompleteTentativeDefinition(VarDecl *d) { + if (diags.hasErrorOccurred()) + return; + + cgm->emitTentativeDefinition(d); +} diff --git a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp index cc65c93f5f16..48ae2a05df70 100644 --- a/clang/lib/CIR/FrontendAction/CIRGenAction.cpp +++ b/clang/lib/CIR/FrontendAction/CIRGenAction.cpp @@ -135,6 +135,10 @@ public: } } } + + void CompleteTentativeDefinition(VarDecl *D) override { + Gen->CompleteTentativeDefinition(D); + } }; } // namespace cir diff --git a/clang/test/CIR/CodeGen/array.cpp b/clang/test/CIR/CodeGen/array.cpp index a4b9398df3b1..18f30b20ff8f 100644 --- a/clang/test/CIR/CodeGen/array.cpp +++ b/clang/test/CIR/CodeGen/array.cpp @@ -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 - -// LLVM: @b = dso_local global [10 x i32] zeroinitializer - -extern int bb[10][5]; -// CIR: cir.global external @bb = #cir.zero : !cir.array 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 @@ -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 +// 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 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]] [ diff --git a/clang/test/CIR/CodeGen/basic.c b/clang/test/CIR/CodeGen/basic.c index 835885cb3f4d..abc1a45fd433 100644 --- a/clang/test/CIR/CodeGen/basic.c +++ b/clang/test/CIR/CodeGen/basic.c @@ -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, ["i", init] {alignment = 4 : i64} // CIR-NEXT: %[[RV:.*]] = cir.alloca !s32i, !cir.ptr, ["__retval"] {alignment = 4 : i64} // CIR-NEXT: cir.store{{.*}} %arg0, %[[I_PTR]] : !s32i, !cir.ptr @@ -288,25 +309,3 @@ size_type max_size(void) { // CHECK: %6 = cir.load{{.*}} %0 : !cir.ptr, !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 diff --git a/clang/test/CIR/CodeGen/basic.cpp b/clang/test/CIR/CodeGen/basic.cpp index 53f13f13853f..ed1c6d364a0e 100644 --- a/clang/test/CIR/CodeGen/basic.cpp +++ b/clang/test/CIR/CodeGen/basic.cpp @@ -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, ["__retval"] {alignment = 4 : i64} // CHECK: %[[I_PTR:.*]] = cir.alloca !s32i, !cir.ptr, ["i"] {alignment = 4 : i64} @@ -145,29 +170,3 @@ void ref_local(short x) { // CHECK: %[[Y_REF_ADDR:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["y", init, const] {alignment = 8 : i64} // CHECK: cir.store{{.*}} %[[ARG]], %[[X_ADDR]] : !s16i, !cir.ptr // CHECK: cir.store{{.*}} %[[X_ADDR]], %[[Y_REF_ADDR]] : !cir.ptr, !cir.ptr> - -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 diff --git a/clang/test/CIR/CodeGen/string-literals.c b/clang/test/CIR/CodeGen/string-literals.c index 873b00d9c9a9..81d2a2759129 100644 --- a/clang/test/CIR/CodeGen/string-literals.c +++ b/clang/test/CIR/CodeGen/string-literals.c @@ -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> : !cir.array +// CIR: cir.global external @[[STR2_GLOBAL:.*]] = #cir.zero : !cir.array +// CIR: cir.global external @[[STR3_GLOBAL:.*]] = #cir.zero : !cir.array + // 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> : !cir.array // CIR: cir.func @f1() // CIR: %[[STR:.*]] = cir.get_global @[[STR1_GLOBAL]] : !cir.ptr> @@ -31,7 +34,6 @@ char *f2() { return ""; } -// CIR: cir.global external @[[STR2_GLOBAL:.*]] = #cir.zero : !cir.array // CIR: cir.func @f2() // CIR: %[[STR2:.*]] = cir.get_global @[[STR2_GLOBAL]] : !cir.ptr> @@ -45,7 +47,6 @@ char *f3() { return "\00"; } -// CIR: cir.global external @[[STR3_GLOBAL:.*]] = #cir.zero : !cir.array // CIR: cir.func @f3() // CIR: %[[STR3:.*]] = cir.get_global @[[STR3_GLOBAL]] : !cir.ptr> diff --git a/clang/test/CIR/CodeGen/struct.c b/clang/test/CIR/CodeGen/struct.c index 9c4a730d19a8..4c18de295c68 100644 --- a/clang/test/CIR/CodeGen/struct.c +++ b/clang/test/CIR/CodeGen/struct.c @@ -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 : !cir.ptr -// 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 : !cir.ptr +// LLVM-DAG: @p = dso_local global ptr null +// OGCG-DAG: @p = global ptr null, align 8 + struct InnerS { int a; char b; diff --git a/clang/test/CIR/Lowering/array.cpp b/clang/test/CIR/Lowering/array.cpp index 4d9161d77663..335042ca68f4 100644 --- a/clang/test/CIR/Lowering/array.cpp +++ b/clang/test/CIR/Lowering/array.cpp @@ -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]; diff --git a/clang/test/CIR/Lowering/hello.c b/clang/test/CIR/Lowering/hello.c index f45beafdcb53..21adf17b73c0 100644 --- a/clang/test/CIR/Lowering/hello.c +++ b/clang/test/CIR/Lowering/hello.c @@ -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