https://github.com/ChuanqiXu9 created 
https://github.com/llvm/llvm-project/pull/218304

Close https://github.com/llvm/llvm-project/issues/212170

See the discussion above for the details.

>From 396a050db6d81b315f303dae6a0339ce7b2b7dbf Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <[email protected]>
Date: Mon, 24 Aug 2026 09:43:17 +0800
Subject: [PATCH] [C++20] [Modules] Preserve the initializer for unused static
 variable in modules

Close https://github.com/llvm/llvm-project/issues/212170

See the discussion above for the details.
---
 clang/lib/Sema/SemaDecl.cpp      | 17 ++++++----
 clang/test/Modules/pr212170.cppm | 54 ++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/Modules/pr212170.cppm

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 5055ef3d1cbb1..a99fcb56d1138 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15378,12 +15378,17 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl 
*var) {
       FinalizeVarWithDestructor(var, RD);
 
   // If this variable must be emitted, add it as an initializer for the current
-  // module.
-  if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty() &&
-      (ModuleScopes.back().Module->isHeaderLikeModule() ||
-       // For named modules, we may only emit non discardable variables.
-       !isDiscardableGVALinkage(Context.GetGVALinkageForVariable(var))))
-    Context.addModuleInitializer(ModuleScopes.back().Module, var);
+  // module. For named modules, discardable inline variables may be deferred
+  // until they are odr-used. Non-inline variables that must be emitted,
+  // including those with side-effecting initialization, must still be emitted
+  // even if they have internal linkage.
+  if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty()) {
+    GVALinkage Linkage = Context.GetGVALinkageForVariable(var);
+    if (ModuleScopes.back().Module->isHeaderLikeModule() ||
+        !isDiscardableGVALinkage(Linkage) ||
+        (Linkage == GVA_Internal && !var->isInline()))
+      Context.addModuleInitializer(ModuleScopes.back().Module, var);
+  }
 
   // Build the bindings if this is a structured binding declaration.
   if (auto *DD = dyn_cast<DecompositionDecl>(var))
diff --git a/clang/test/Modules/pr212170.cppm b/clang/test/Modules/pr212170.cppm
new file mode 100644
index 0000000000000..65442d011cc53
--- /dev/null
+++ b/clang/test/Modules/pr212170.cppm
@@ -0,0 +1,54 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -emit-module-interface %t/test.cppm -o %t/test.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -emit-llvm %t/test.pcm -o - | FileCheck %t/test.cppm \
+// RUN:   --implicit-check-not=inline_bar \
+// RUN:   --implicit-check-not=static_inline_bar
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -fmodule-file=test=%t/test.pcm -emit-llvm %t/use.cpp -o - | \
+// RUN:   FileCheck %t/use.cpp
+
+// https://github.com/llvm/llvm-project/issues/212170
+
+//--- test.cppm
+
+export module test;
+
+int side_effect();
+
+static int bar = side_effect();
+[[gnu::used]] static int used_bar = side_effect();
+
+inline int inline_bar = side_effect();
+static inline int static_inline_bar = side_effect();
+
+// CHECK-DAG: @_ZL3bar = internal global i32 0
+// CHECK-DAG: @_ZL8used_bar = internal global i32 0
+
+// CHECK-LABEL: define internal void @__cxx_global_var_init()
+// CHECK: call{{.*}} @_ZW4test11side_effectv()
+// CHECK: store i32 {{.*}}, ptr @_ZL3bar
+
+// CHECK-LABEL: define internal void @__cxx_global_var_init.1()
+// CHECK: call{{.*}} @_ZW4test11side_effectv()
+// CHECK: store i32 {{.*}}, ptr @_ZL8used_bar
+
+// CHECK-LABEL: define void @_ZGIW4test()
+// CHECK: call void @__cxx_global_var_init()
+// CHECK: call void @__cxx_global_var_init.1()
+
+//--- use.cpp
+
+import test;
+
+int main() {}
+
+// The consumer calls the module initializer but does not emit copies of the
+// module's internal variables or their initialization functions.
+// CHECK-NOT: @_ZL3bar
+// CHECK-NOT: @_ZL8used_bar
+// CHECK: declare void @_ZGIW4test()
+// CHECK-LABEL: define internal void @_GLOBAL__sub_I_use.cpp()
+// CHECK: call void @_ZGIW4test()

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

Reply via email to