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/3] [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/3] [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/3] 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));
     }
   }
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to