https://github.com/koparasy updated 
https://github.com/llvm/llvm-project/pull/225565

>From 6b5cc699e370080b1588b2a20980cec4e68c7ad2 Mon Sep 17 00:00:00 2001
From: Konstantinos Parasyris <[email protected]>
Date: Tue, 22 Sep 2026 17:32:52 -0700
Subject: [PATCH] [CIR] Fix assertion on Microsoft-ABI C++20 named module
 initializer

---
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        | 15 +++++-
 .../Dialect/Transforms/LoweringPrepare.cpp    | 18 ++------
 .../cxx20-module-initializer-ms-abi.cppm      | 46 +++++++++++++++++++
 3 files changed, 64 insertions(+), 15 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/cxx20-module-initializer-ms-abi.cppm

diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index c644edda897a3..61d5b03f393b0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -3980,8 +3980,19 @@ 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.
+  // stash it on the ModuleOp so LoweringPrepare (which runs without a live
+  // ASTContext) can read it back as an attribute.  This attribute is the only
+  // channel through which the named-module initializer reaches lowering: its
+  // presence tells LoweringPrepare both what to call the global-init function
+  // and that the function needs external linkage, and its absence selects the
+  // `_GLOBAL__sub_I_` form.  Lowering therefore never has to rediscover the
+  // module from the AST.
+  //
+  // The mangler-kind check mirrors classic codegen's `CXX20ModuleInits` (see
+  // CodeGenModule.cpp), which only enables C++20 module initializers for the
+  // Itanium mangler because no Microsoft mangling for them has been settled
+  // on yet.  Non-Itanium named modules fall back to `_GLOBAL__sub_I_` exactly
+  // as they do in classic codegen.
   if (langOpts.CPlusPlusModules &&
       getCXXABI().getMangleContext().getKind() ==
           clang::ItaniumMangleContext::MK_Itanium) {
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 2ab71608329ab..bca424b4a1411 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -14,9 +14,7 @@
 #include "mlir/IR/Location.h"
 #include "mlir/IR/Value.h"
 #include "clang/AST/ASTContext.h"
-#include "clang/AST/Mangle.h"
 #include "clang/Basic/Cuda.h"
-#include "clang/Basic/Module.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/Specifiers.h"
 #include "clang/Basic/TargetCXXABI.h"
@@ -1996,21 +1994,15 @@ void LoweringPreparePass::buildCXXGlobalInitFunc() {
   // with priority (TBD).  Module implementation units behave the same
   // way as a non-modular TU with imports.
   // 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).
+  // stored as a module-level attribute.  Its presence is what marks this
+  // module as a named-module interface unit, so the name and the external
+  // linkage that goes with it both come from the attribute and this pass needs
+  // no live ASTContext.  Modules built directly from textual CIR can opt in to
+  // the module-init form by setting the same attribute.
   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());
-    cast<clang::ItaniumMangleContext>(*mangleCtx)
-        .mangleModuleInitializer(astCtx->getCurrentNamedModule(), out);
-    linkage = cir::GlobalLinkageKind::ExternalLinkage;
   } else {
     fnName += "_GLOBAL__sub_I_";
     fnName += getTransformedFileName(mlirModule);
diff --git a/clang/test/CIR/CodeGen/cxx20-module-initializer-ms-abi.cppm 
b/clang/test/CIR/CodeGen/cxx20-module-initializer-ms-abi.cppm
new file mode 100644
index 0000000000000..56df8dcb6c9f8
--- /dev/null
+++ b/clang/test/CIR/CodeGen/cxx20-module-initializer-ms-abi.cppm
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-pc-windows-msvc -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR 
--implicit-check-not=_ZGIW
+
+// Classic codegen only enables C++20 module initializers for the Itanium
+// mangler, because no Microsoft mangling for them has been settled on yet (see
+// CXX20ModuleInits in CodeGenModule.cpp).  A named module built for the
+// Microsoft C++ ABI therefore gets the ordinary `_GLOBAL__sub_I_` initializer
+// with internal linkage, exactly as a non-modular translation unit would, and
+// CIRGen matches that by only emitting the cir.cxx_module_init_fn_name
+// attribute for the Itanium mangler.
+//
+// LoweringPrepare used to rediscover the named module from the AST whenever
+// that attribute was absent, and mangled the name with an unguarded
+// cast<ItaniumMangleContext>, which asserted for this target.  The attribute 
is
+// now the only channel through which the named-module initializer reaches
+// lowering, so this compiles rather than crashing.
+//
+// For reference, classic codegen emits for this input:
+//   @llvm.global_ctors = ... { i32 65535, ptr @_GLOBAL__sub_I_<file>, ptr 
null }
+//   define internal void @"??__Ex@@YAXXZ"() { %call = call @"?foo@@YAHXZ"()
+//                                             store i32 %call, ptr @"?x@@3HA" 
}
+//   define internal void @_GLOBAL__sub_I_<file>() { call void 
@"??__Ex@@YAXXZ"() }
+// CIR does not yet apply the Microsoft dynamic-initializer mangling to the
+// per-variable initializer, naming it __cxx_global_var_init instead; that gap
+// is unrelated to named modules and reproduces for a non-modular TU too.
+
+export module A;
+
+int foo();
+int x = foo();
+
+// The Itanium-only attribute must not be emitted for the Microsoft mangler.
+// CIR-NOT: cir.cxx_module_init_fn_name
+
+// CIR: cir.global_ctors = [#cir.global_ctor<"_GLOBAL__sub_I_{{.*}}", 65535>]
+
+// CIR:      cir.func internal private @__cxx_global_var_init()
+// CIR:        %[[X:.*]] = cir.get_global @"?x@@3HA"
+// CIR:        %[[CALL:.*]] = cir.call @"?foo@@YAHXZ"()
+// CIR:        cir.store align(4) %[[CALL]], %[[X]]
+
+// The fallback initializer has internal linkage, unlike the external-linkage
+// initializer a named-module interface unit gets under the Itanium mangler, 
and
+// it just calls the per-variable initializer.
+// CIR:      cir.func internal private @_GLOBAL__sub_I_
+// CIR-NEXT:   cir.call @__cxx_global_var_init()

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

Reply via email to