[clang] [CIR] Correct the behavior of 'shouldEmitFunction' with always/noinline (PR #224378)
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 21 07:08:37 PDT 2026
https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/224378
>From dfd409d393eca83f77be793ea7884df70fac1c54 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Thu, 17 Sep 2026 09:19:59 -0700
Subject: [PATCH 1/2] [CIR] Correct the behavior of 'shouldEmitFunction' with
always/noinline
These attributes modified the linkage of functions in certain cases that
caused us to, ironically, skip emitting something when we picked it up
via PCH during self-build. This patch just copies/pastes the code from
classic codegen.
---
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 14 +++++
.../noinline-available-externally-skip.cpp | 51 +++++++++++++++++++
2 files changed, 65 insertions(+)
create mode 100644 clang/test/CIR/CodeGen/noinline-available-externally-skip.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 3af6ce4ce6e94..03a799ccc74f1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1705,6 +1705,20 @@ bool CIRGenModule::shouldEmitFunction(GlobalDecl gd) {
if (fd->isInlineBuiltinDeclaration())
return true;
+ if (codeGenOpts.OptimizationLevel == 0 && !fd->hasAttr<AlwaysInlineAttr>())
+ return false;
+
+ // We don't import function bodies from other named module units since that
+ // behavior may break ABI compatibility of the current unit.
+ if (const Module *m = fd->getOwningModule();
+ m && m->getTopLevelModule()->isNamedModule() &&
+ getASTContext().getCurrentNamedModule() != m->getTopLevelModule()) {
+ errorNYI(fd->getSourceRange(), "should emit function in a named module");
+ }
+
+ if (fd->hasAttr<NoInlineAttr>())
+ return false;
+
// PR9614 / glibc btowc workaround: an available_externally function whose
// body just calls itself (via asm label or __builtin_* lowering on the
// same name) is not a valid stand-in for the real implementation. Drop
diff --git a/clang/test/CIR/CodeGen/noinline-available-externally-skip.cpp b/clang/test/CIR/CodeGen/noinline-available-externally-skip.cpp
new file mode 100644
index 0000000000000..0a7821c61c150
--- /dev/null
+++ b/clang/test/CIR/CodeGen/noinline-available-externally-skip.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -O0 -disable-llvm-passes -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR,CIRO0
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -O0 -disable-llvm-passes -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM,LLVMO0
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O0 -disable-llvm-passes -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM,LLVMO0
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -O2 -disable-llvm-passes -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR,CIRO2
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -O2 -disable-llvm-passes -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM,LLVMO2
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -disable-llvm-passes -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM,LLVMO2
+
+template <typename T>
+class Holder {
+ T val;
+public:
+ explicit Holder(T v) : val(v) {}
+ // NOT defined.
+ // CIR-DAG: cir.func private @_ZNK6HolderIiE4dumpEv{{.*}}attributes {{{.*}}}{{[^{]*}}{{$}}
+ // LLVM-DAG: declare void @_ZNK6HolderIiE4dumpEv
+ __attribute__((noinline)) void dump() const {}
+
+ // ONLY defined in O2:
+ // CIRO0-DAG: cir.func private @_ZNK6HolderIiE4showEv{{.*}}attributes {{{.*}}}{{[^{]*}}{{$}}
+ // CIRO2-DAG: cir.func available_externally @_ZNK6HolderIiE4showEv{{.*}}attributes {{{.*}}} {
+ // LLVMO0-DAG: declare void @_ZNK6HolderIiE4showEv
+ // LLVMO2-DAG: define available_externally void @_ZNK6HolderIiE4showEv
+ void show() const {}
+
+ // CIR-DAG: cir.func always_inline available_externally @_ZNK6HolderIiE18dump_always_inlineEv{{.*}}attributes {{{.*}}} {
+ // LLVM-DAG: define available_externally void @_ZNK6HolderIiE18dump_always_inlineEv
+ __attribute__((always_inline)) void dump_always_inline() const {}
+};
+
+// Suppresses instantiation in this TU; dump() and show() are available_externally.
+extern template class Holder<int>;
+
+// Normal Definition (wildcard is no_inline, added in O0)
+// CIR-DAG: cir.func {{.*}}dso_local @_Z6callerP6HolderIiE{{.*}} attributes {{{.*}}} {
+// LLVM-DAG: define {{.*}}dso_local void @_Z6callerP6HolderIiE
+void caller(Holder<int> *h) {
+ h->dump_always_inline();
+ h->dump();
+ h->show();
+}
>From 2473db03cbc18f1ea3df5112ad99aaffc0843af1 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Fri, 18 Sep 2026 13:17:17 -0700
Subject: [PATCH 2/2] Fix test that will fail when updating to main
---
clang/test/CIR/CodeGen/noinline-available-externally-skip.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/CIR/CodeGen/noinline-available-externally-skip.cpp b/clang/test/CIR/CodeGen/noinline-available-externally-skip.cpp
index 0a7821c61c150..b2f1a57695e67 100644
--- a/clang/test/CIR/CodeGen/noinline-available-externally-skip.cpp
+++ b/clang/test/CIR/CodeGen/noinline-available-externally-skip.cpp
@@ -33,7 +33,7 @@ class Holder {
// LLVMO2-DAG: define available_externally void @_ZNK6HolderIiE4showEv
void show() const {}
- // CIR-DAG: cir.func always_inline available_externally @_ZNK6HolderIiE18dump_always_inlineEv{{.*}}attributes {{{.*}}} {
+ // CIR-DAG: cir.func always_inline {{.*}}available_externally @_ZNK6HolderIiE18dump_always_inlineEv{{.*}}attributes {{{.*}}} {
// LLVM-DAG: define available_externally void @_ZNK6HolderIiE18dump_always_inlineEv
__attribute__((always_inline)) void dump_always_inline() const {}
};
More information about the cfe-commits
mailing list