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

Reply via email to