https://github.com/koparasy updated https://github.com/llvm/llvm-project/pull/215921
>From c4371f6db5f1f8afeb6b8bcf72bf02b307363f87 Mon Sep 17 00:00:00 2001 From: Konstantinos Parasyris <[email protected]> Date: Wed, 12 Aug 2026 16:23:27 -0700 Subject: [PATCH 1/4] [CIR] Materialize C++20 module init fn name as module attribute --- .../clang/CIR/Dialect/IR/CIRDialect.td | 3 +++ clang/lib/CIR/CodeGen/CIRGenModule.cpp | 20 +++++++++++++++++ .../Dialect/Transforms/LoweringPrepare.cpp | 13 ++++++++--- .../CIR/CodeGen/cxx20-module-initializer.cppm | 22 +++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 clang/test/CIR/CodeGen/cxx20-module-initializer.cppm diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td index de90fed45f170..c546e9535a50f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -78,6 +78,9 @@ def CIR_Dialect : Dialect { static llvm::StringRef getArgAttrsAttrName() { return "arg_attrs"; } static llvm::StringRef getRecordLayoutsAttrName() { return "cir.record_layouts"; } static llvm::StringRef getCUDABinaryHandleAttrName() { return "cir.cu.binary_handle"; } + // Mangled symbol name of the C++20 named-module initializer function, + // precomputed by CIRGen so later passes don't need a live ASTContext. + static llvm::StringRef getCXXModuleInitFnNameAttrName() { return "cir.cxx_module_init_fn_name"; } static llvm::StringRef getMustTailAttrName() { return "musttail"; } static llvm::StringRef getCatchCopyThunkAttrName() { return "cir.eh.catch_copy_thunk"; } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index ae9cad0b7c30f..a5fc3a9bae7cb 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -24,9 +24,11 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/DeclOpenACC.h" #include "clang/AST/GlobalDecl.h" +#include "clang/AST/Mangle.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtOpenMP.h" #include "clang/Basic/DiagnosticFrontend.h" +#include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" @@ -3750,6 +3752,24 @@ void CIRGenModule::release() { emitLLVMUsed(); + // Precompute the mangled C++20 named-module initializer function name and + // stash it on the ModuleOp so LoweringPrepare (which may run without a live + // ASTContext in split-compilation flows) can read it back as an attribute. + if (langOpts.CPlusPlusModules && + getCXXABI().getMangleContext().getKind() == + clang::ItaniumMangleContext::MK_Itanium) { + if (clang::Module *primary = astContext.getCurrentNamedModule(); + primary && !primary->isModuleImplementation()) { + llvm::SmallString<256> fnName; + llvm::raw_svector_ostream out(fnName); + cast<clang::ItaniumMangleContext>(getCXXABI().getMangleContext()) + .mangleModuleInitializer(primary, out); + theModule->setAttr( + cir::CIRDialect::getCXXModuleInitFnNameAttrName(), + builder.getStringAttr(fnName)); + } + } + // Classic codegen calls `checkAliases` here to validate any alias // definitions emitted during codegen. assert(!cir::MissingFeatures::checkAliases()); diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 12d544fecbcc9..8ec9f442bc892 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -1866,9 +1866,16 @@ void LoweringPreparePass::buildCXXGlobalInitFunc() { // and makes sure these symbols appear lexicographically behind the symbols // with priority (TBD). Module implementation units behave the same // way as a non-modular TU with imports. - // TODO: check CXX20ModuleInits - if (astCtx->getCurrentNamedModule() && - !astCtx->getCurrentNamedModule()->isModuleImplementation()) { + // The C++20 named-module init function name is precomputed by CIRGen and + // stored as a module-level attribute, so this pass does not need a live + // ASTContext in split-compilation flows. Fall back to the AST-based path + // only when the attribute is absent (e.g. tests that bypass CIRGen). + if (auto fnNameAttr = mlirModule->getAttrOfType<mlir::StringAttr>( + cir::CIRDialect::getCXXModuleInitFnNameAttrName())) { + fnName += fnNameAttr.getValue(); + linkage = cir::GlobalLinkageKind::ExternalLinkage; + } else if (astCtx && astCtx->getCurrentNamedModule() && + !astCtx->getCurrentNamedModule()->isModuleImplementation()) { llvm::raw_svector_ostream out(fnName); std::unique_ptr<clang::MangleContext> mangleCtx( astCtx->createMangleContext()); diff --git a/clang/test/CIR/CodeGen/cxx20-module-initializer.cppm b/clang/test/CIR/CodeGen/cxx20-module-initializer.cppm new file mode 100644 index 0000000000000..2122a5e97636d --- /dev/null +++ b/clang/test/CIR/CodeGen/cxx20-module-initializer.cppm @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -std=c++20 -triple %itanium_abi_triple -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR + +// CIRGen precomputes the mangled C++20 named-module initializer function +// name and stores it as a module-level attribute so LoweringPrepare can +// build the initializer without a live ASTContext after split-compilation. +// The dynamic initializer below forces LoweringPrepare to actually emit that +// initializer function, which must have external linkage for a named-module +// interface unit. + +export module A; + +int foo(); +int x = foo(); + +// CIR: module +// CIR-SAME: cir.cxx_module_init_fn_name = "_ZGIW1A" + +// The initializer for a named-module interface unit has external linkage. +// (Internal linkage would render as "cir.func internal private", so matching +// "cir.func private" immediately after the name asserts external linkage.) +// CIR: cir.func private @_ZGIW1A() >From 2a56c8598030bb845b0cfe3786fff8776afa613e Mon Sep 17 00:00:00 2001 From: Konstantinos Parasyris <[email protected]> Date: Wed, 12 Aug 2026 16:23:28 -0700 Subject: [PATCH 2/4] [CIR] Emit StaticLocalInfoAttr for static-local guarded globals --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 46 +++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenCXX.cpp | 13 +++++- clang/test/CIR/CodeGen/static-local-info.cpp | 35 ++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 clang/test/CIR/CodeGen/static-local-info.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 253421ab764ff..43c3b8d13de97 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1935,6 +1935,52 @@ def CIR_ASTVarDeclAttr : CIR_AST<"VarDecl", "var.decl", [ ASTVarDeclInterface ]>; +//===----------------------------------------------------------------------===// +// StaticLocalInfoAttr +//===----------------------------------------------------------------------===// + +def CIR_StaticLocalInfoAttr + : CIR_Attr<"StaticLocalInfo", "static_local_info", + [ASTVarDeclInterface]> { + let summary = "AST-free cache of static-local VarDecl facts."; + let description = [{ + Materialized form of the facts `cir::ASTVarDeclInterface` exposes, so + that post-CIRGen passes (notably LoweringPrepare) can query them without + a live `clang::ASTContext`. Emitted by CIRGen for static-local guarded + globals in place of the AST-backed `ASTVarDeclAttr`. + + The TLS and template-specialization kinds are encoded as plain integers + matching the underlying `clang::VarDecl::TLSKind` and + `clang::TemplateSpecializationKind` enumerators. + }]; + + let parameters = (ins + "bool":$is_local_var_decl, + "uint32_t":$tls, + "bool":$is_inline, + "uint32_t":$tsk + ); + + let assemblyFormat = [{ + `<` struct($is_local_var_decl, $tls, $is_inline, $tsk) `>` + }]; + + let extraClassDeclaration = [{ + // Satisfy ASTVarDeclInterface by reading cached fields instead of an + // AST-backed VarDecl pointer. + bool isLocalVarDecl() const { return getIsLocalVarDecl(); } + clang::VarDecl::TLSKind getTLSKind() const { + return static_cast<clang::VarDecl::TLSKind>(getTls()); + } + bool isInline() const { return getIsInline(); } + clang::TemplateSpecializationKind getTemplateSpecializationKind() const { + return static_cast<clang::TemplateSpecializationKind>(getTsk()); + } + }]; + + let canHaveIllegalCXXABIType = 0; +} + //===----------------------------------------------------------------------===// // AnnotationAttr //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp index 812ecc1016dcc..50f0b5ac3e003 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp @@ -265,7 +265,18 @@ void CIRGenModule::emitCXXSpecialVarDeclInit(const VarDecl *varDecl, // expects "this" in the "generic" address space. assert(!cir::MissingFeatures::addressSpace()); - addr.setAstAttr(cir::ASTVarDeclAttr::get(&getMLIRContext(), varDecl)); + // LoweringPrepare reads VarDecl facts back through ASTVarDeclInterface, but + // only for static-local guarded globals. For those, emit the materialized + // StaticLocalInfoAttr so the facts survive without a live ASTContext (e.g. + // serialized CIR in split-compilation flows). Other globals keep the + // AST-backed attribute; their $ast is never queried after CIRGen. + if (addr.getStaticLocalGuard().has_value()) + addr.setAstAttr(cir::StaticLocalInfoAttr::get( + &getMLIRContext(), varDecl->isLocalVarDecl(), + static_cast<uint32_t>(varDecl->getTLSKind()), varDecl->isInline(), + static_cast<uint32_t>(varDecl->getTemplateSpecializationKind()))); + else + addr.setAstAttr(cir::ASTVarDeclAttr::get(&getMLIRContext(), varDecl)); if (!ty->isReferenceType()) { assert(!cir::MissingFeatures::openMP()); diff --git a/clang/test/CIR/CodeGen/static-local-info.cpp b/clang/test/CIR/CodeGen/static-local-info.cpp new file mode 100644 index 0000000000000..87d198d1111f0 --- /dev/null +++ b/clang/test/CIR/CodeGen/static-local-info.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir \ +// RUN: -emit-cir %s -o - | FileCheck %s + +// CIRGen attaches the VarDecl facts LoweringPrepare needs (isLocalVarDecl, +// TLSKind, isInline, TemplateSpecializationKind) to static-local guarded +// globals as a materialized #cir.static_local_info attribute, so the facts +// survive without a live ASTContext. The attribute is emitted directly at +// CIRGen time; there is no separate materialization pass and no AST-backed +// #cir.var.decl placeholder is left on these globals. + +struct HasCtor { + HasCtor(); + int x; +}; + +int regular() { + static HasCtor s; + return s.x; +} + +int tls() { + static thread_local HasCtor s; + return s.x; +} + +// A static local is always a local var decl; the guarded global therefore +// carries is_local_var_decl = true and never the AST-backed placeholder. +// CHECK-NOT: #cir.var.decl + +// The thread_local static local materializes a non-default TLS kind. +// CHECK: @_ZZ3tlsvE1s +// CHECK-SAME: ast = #cir.static_local_info<is_local_var_decl = true, tls = 2, is_inline = false, tsk = 0> + +// CHECK: @_ZZ7regularvE1s +// CHECK-SAME: ast = #cir.static_local_info<is_local_var_decl = true, tls = 0, is_inline = false, tsk = 0> >From f20a01eb7b04b2bfef04ff5b6ec8adaf3e98565c Mon Sep 17 00:00:00 2001 From: Konstantinos Parasyris <[email protected]> Date: Wed, 12 Aug 2026 16:44:12 -0700 Subject: [PATCH 3/4] Format --- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index a5fc3a9bae7cb..989c0176390b7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -3764,9 +3764,8 @@ void CIRGenModule::release() { llvm::raw_svector_ostream out(fnName); cast<clang::ItaniumMangleContext>(getCXXABI().getMangleContext()) .mangleModuleInitializer(primary, out); - theModule->setAttr( - cir::CIRDialect::getCXXModuleInitFnNameAttrName(), - builder.getStringAttr(fnName)); + theModule->setAttr(cir::CIRDialect::getCXXModuleInitFnNameAttrName(), + builder.getStringAttr(fnName)); } } >From 0c369a7253e2881fa06728ca6c1a75b7aa1f61e4 Mon Sep 17 00:00:00 2001 From: Konstantinos Parasyris <[email protected]> Date: Thu, 13 Aug 2026 18:45:25 -0700 Subject: [PATCH 4/4] [CIR] Address review: standalone StaticLocalInfoAttr on GlobalOp field --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 36 +++++++++++-------- clang/include/clang/CIR/Dialect/IR/CIROps.td | 1 + clang/lib/CIR/CodeGen/CIRGenCXX.cpp | 16 ++++----- .../Dialect/Transforms/LoweringPrepare.cpp | 26 ++++++++------ clang/test/CIR/CodeGen/static-local-info.cpp | 20 +++++------ clang/test/CIR/IR/static-local-info.cir | 20 +++++++++++ 6 files changed, 75 insertions(+), 44 deletions(-) create mode 100644 clang/test/CIR/IR/static-local-info.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 43c3b8d13de97..88cc53756eab1 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1940,18 +1940,29 @@ def CIR_ASTVarDeclAttr : CIR_AST<"VarDecl", "var.decl", [ //===----------------------------------------------------------------------===// def CIR_StaticLocalInfoAttr - : CIR_Attr<"StaticLocalInfo", "static_local_info", - [ASTVarDeclInterface]> { - let summary = "AST-free cache of static-local VarDecl facts."; + : CIR_Attr<"StaticLocalInfo", "static_local_info"> { + let summary = "Static-local variable facts needed by later lowering"; let description = [{ - Materialized form of the facts `cir::ASTVarDeclInterface` exposes, so - that post-CIRGen passes (notably LoweringPrepare) can query them without - a live `clang::ASTContext`. Emitted by CIRGen for static-local guarded - globals in place of the AST-backed `ASTVarDeclAttr`. + Holds the subset of a static-local variable's declaration facts that + post-CIRGen lowering needs: whether it is a local variable declaration + (`is_local_var_decl`), its thread-local storage kind (`tls`), whether it + is inline (`is_inline`), and its template-specialization kind (`tsk`). + `tls` and `tsk` are stored as plain integers matching the + `clang::VarDecl::TLSKind` and `clang::TemplateSpecializationKind` + enumerators. - The TLS and template-specialization kinds are encoded as plain integers - matching the underlying `clang::VarDecl::TLSKind` and - `clang::TemplateSpecializationKind` enumerators. + Example: + ``` + cir.global ... @x = ... + {static_local_info = #cir.static_local_info< + is_local_var_decl = true, tls = 0, is_inline = false, tsk = 0>} + ``` + + Because it stores plain data rather than a `clang::VarDecl` pointer, it + round-trips through textual CIR and stays valid once the AST is gone. + This is what lets LoweringPrepare consume these facts without a live + `clang::ASTContext`; the `$ast` `ASTVarDeclAttr` remains a separate, + live-AST handle for consumers that need arbitrary AST properties. }]; let parameters = (ins @@ -1966,13 +1977,10 @@ def CIR_StaticLocalInfoAttr }]; let extraClassDeclaration = [{ - // Satisfy ASTVarDeclInterface by reading cached fields instead of an - // AST-backed VarDecl pointer. - bool isLocalVarDecl() const { return getIsLocalVarDecl(); } + // Typed accessors over the stored integer fields. clang::VarDecl::TLSKind getTLSKind() const { return static_cast<clang::VarDecl::TLSKind>(getTls()); } - bool isInline() const { return getIsInline(); } clang::TemplateSpecializationKind getTemplateSpecializationKind() const { return static_cast<clang::TemplateSpecializationKind>(getTsk()); } diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 599ea50c85267..b81662f48adfe 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -3356,6 +3356,7 @@ def CIR_GlobalOp : CIR_Op<"global", [ OptionalAttr<CIR_StaticLocalGuardAttr>:$static_local_guard, OptionalAttr<I64Attr>:$alignment, OptionalAttr<ASTVarDeclInterface>:$ast, + OptionalAttr<CIR_StaticLocalInfoAttr>:$static_local_info, OptionalAttr<StrAttr>:$section, OptionalAttr<CIR_AnnotationArrayAttr>:$annotations, OptionalAttr<FlatSymbolRefAttr>:$aliasee, diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp index 50f0b5ac3e003..fabb78e4a187c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp @@ -265,18 +265,18 @@ void CIRGenModule::emitCXXSpecialVarDeclInit(const VarDecl *varDecl, // expects "this" in the "generic" address space. assert(!cir::MissingFeatures::addressSpace()); - // LoweringPrepare reads VarDecl facts back through ASTVarDeclInterface, but - // only for static-local guarded globals. For those, emit the materialized - // StaticLocalInfoAttr so the facts survive without a live ASTContext (e.g. - // serialized CIR in split-compilation flows). Other globals keep the - // AST-backed attribute; their $ast is never queried after CIRGen. + // Attach the AST handle for consumers that need arbitrary AST properties. + addr.setAstAttr(cir::ASTVarDeclAttr::get(&getMLIRContext(), varDecl)); + + // For static-local guarded globals, also materialize the specific facts + // LoweringPrepare needs into a serializable attribute, so that lowering can + // run without a live ASTContext (e.g. on serialized CIR in split-compilation + // flows). This is orthogonal to the AST handle above. if (addr.getStaticLocalGuard().has_value()) - addr.setAstAttr(cir::StaticLocalInfoAttr::get( + addr.setStaticLocalInfoAttr(cir::StaticLocalInfoAttr::get( &getMLIRContext(), varDecl->isLocalVarDecl(), static_cast<uint32_t>(varDecl->getTLSKind()), varDecl->isInline(), static_cast<uint32_t>(varDecl->getTemplateSpecializationKind()))); - else - addr.setAstAttr(cir::ASTVarDeclAttr::get(&getMLIRContext(), varDecl)); if (!ty->isReferenceType()) { assert(!cir::MissingFeatures::openMP()); diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 8ec9f442bc892..55c6efff2aba1 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -403,7 +403,7 @@ struct LoweringPreparePass /// following OG's ItaniumCXXABI::EmitGuardedInit skeleton. void emitCXXGuardedInitIf(CIRBaseBuilderTy &builder, cir::GlobalOp globalOp, mlir::Region &ctorRegion, mlir::Region &dtorRegion, - cir::ASTVarDeclInterface varDecl, + bool isLocalVarDecl, mlir::Value guardPtr, cir::PointerType guardPtrTy, bool threadsafe) { auto loc = globalOp->getLoc(); @@ -481,7 +481,7 @@ struct LoweringPreparePass mlir::ValueRange{guardPtr}); builder.createYield(loc); - } else if (!varDecl.isLocalVarDecl()) { + } else if (!isLocalVarDecl) { // For non-local variables, store 1 into the first byte of the guard // variable before the object initialization begins so that references // to the variable during initialization don't restart initialization. @@ -1298,9 +1298,12 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp, cir::LocalInitOp localInitOp) { CIRBaseBuilderTy builder(getContext()); - std::optional<cir::ASTVarDeclInterface> astOption = globalOp.getAst(); - assert(astOption.has_value()); - cir::ASTVarDeclInterface varDecl = astOption.value(); + // Static-local facts are materialized into a serializable attribute by + // CIRGen, so this pass does not need a live ASTContext to read them. + std::optional<cir::StaticLocalInfoAttr> infoOption = + globalOp.getStaticLocalInfo(); + assert(infoOption.has_value()); + cir::StaticLocalInfoAttr info = infoOption.value(); builder.setInsertionPointAfter(localInitOp); mlir::Block *localInitBlock = builder.getInsertionBlock(); @@ -1315,8 +1318,8 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp, // Inline variables that weren't instantiated from variable templates have // partially-ordered initialization within their translation unit. bool nonTemplateInline = - varDecl.isInline() && - !clang::isTemplateInstantiation(varDecl.getTemplateSpecializationKind()); + info.getIsInline() && + !clang::isTemplateInstantiation(info.getTemplateSpecializationKind()); // Inline namespace-scope variables require guarded initialization in a // __cxx_global_var_init function. This is not yet implemented. @@ -1330,8 +1333,8 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp, // inline variables; other global initialization is always single-threaded // or (through lazy dynamic loading in multiple threads) unsequenced. bool threadsafe = astCtx->getLangOpts().ThreadsafeStatics && - (varDecl.isLocalVarDecl() || nonTemplateInline) && - !varDecl.getTLSKind(); + (info.getIsLocalVarDecl() || nonTemplateInline) && + !info.getTLSKind(); // If we have a global variable with internal linkage and thread-safe statics // are disabled, we can just let the guard variable be of type i8. @@ -1340,7 +1343,7 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp, // Create the guard variable if we don't already have it. cir::GlobalOp guard = getOrCreateStaticLocalDeclGuardAddress( builder, globalOp, globalOp.getStaticLocalGuard()->getName().getValue(), - varDecl.isLocalVarDecl(), useInt8GuardVariable); + info.getIsLocalVarDecl(), useInt8GuardVariable); if (!guard) { // Error was already emitted, just restore the terminator and return. localInitBlock->push_back(ret); @@ -1429,7 +1432,8 @@ void LoweringPreparePass::handleStaticLocal(cir::GlobalOp globalOp, builder, globalOp.getLoc(), needsInit, /*withElseRegion=*/false, [&](mlir::OpBuilder &, mlir::Location) { emitCXXGuardedInitIf(builder, globalOp, localInitOp.getCtorRegion(), - localInitOp.getDtorRegion(), varDecl, guardPtr, + localInitOp.getDtorRegion(), + info.getIsLocalVarDecl(), guardPtr, builder.getPointerTo(guard.getSymType()), threadsafe); }); diff --git a/clang/test/CIR/CodeGen/static-local-info.cpp b/clang/test/CIR/CodeGen/static-local-info.cpp index 87d198d1111f0..458fb3a4cc8d6 100644 --- a/clang/test/CIR/CodeGen/static-local-info.cpp +++ b/clang/test/CIR/CodeGen/static-local-info.cpp @@ -3,10 +3,9 @@ // CIRGen attaches the VarDecl facts LoweringPrepare needs (isLocalVarDecl, // TLSKind, isInline, TemplateSpecializationKind) to static-local guarded -// globals as a materialized #cir.static_local_info attribute, so the facts -// survive without a live ASTContext. The attribute is emitted directly at -// CIRGen time; there is no separate materialization pass and no AST-backed -// #cir.var.decl placeholder is left on these globals. +// globals as a #cir.static_local_info attribute, so the facts survive without +// a live ASTContext. This is orthogonal to the #cir.var.decl AST handle, which +// is still attached for consumers that need arbitrary AST properties. struct HasCtor { HasCtor(); @@ -23,13 +22,12 @@ int tls() { return s.x; } -// A static local is always a local var decl; the guarded global therefore -// carries is_local_var_decl = true and never the AST-backed placeholder. -// CHECK-NOT: #cir.var.decl - -// The thread_local static local materializes a non-default TLS kind. +// The thread_local static local materializes a non-default TLS kind, alongside +// the retained AST handle. // CHECK: @_ZZ3tlsvE1s -// CHECK-SAME: ast = #cir.static_local_info<is_local_var_decl = true, tls = 2, is_inline = false, tsk = 0> +// CHECK-SAME: ast = #cir.var.decl.ast +// CHECK-SAME: static_local_info = #cir.static_local_info<is_local_var_decl = true, tls = 2, is_inline = false, tsk = 0> // CHECK: @_ZZ7regularvE1s -// CHECK-SAME: ast = #cir.static_local_info<is_local_var_decl = true, tls = 0, is_inline = false, tsk = 0> +// CHECK-SAME: ast = #cir.var.decl.ast +// CHECK-SAME: static_local_info = #cir.static_local_info<is_local_var_decl = true, tls = 0, is_inline = false, tsk = 0> diff --git a/clang/test/CIR/IR/static-local-info.cir b/clang/test/CIR/IR/static-local-info.cir new file mode 100644 index 0000000000000..bb100c2f3efc0 --- /dev/null +++ b/clang/test/CIR/IR/static-local-info.cir @@ -0,0 +1,20 @@ +// RUN: cir-opt %s --verify-roundtrip | FileCheck %s + +// #cir.static_local_info holds plain data (no AST pointer), so it parses and +// prints without a live ASTContext. Check that each field round-trips. + +!s32i = !cir.int<s, 32> + +module { + cir.global "private" internal @regular = #cir.int<0> : !s32i + {static_local_info = #cir.static_local_info< + is_local_var_decl = true, tls = 0, is_inline = false, tsk = 0>} + // CHECK: @regular + // CHECK-SAME: static_local_info = #cir.static_local_info<is_local_var_decl = true, tls = 0, is_inline = false, tsk = 0> + + cir.global "private" internal @tls = #cir.int<0> : !s32i + {static_local_info = #cir.static_local_info< + is_local_var_decl = true, tls = 2, is_inline = true, tsk = 1>} + // CHECK: @tls + // CHECK-SAME: static_local_info = #cir.static_local_info<is_local_var_decl = true, tls = 2, is_inline = true, tsk = 1> +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
