https://github.com/mathew1046 created https://github.com/llvm/llvm-project/pull/228962
Importing a named module before including a GNU-inline wrapper header can retain a definition owned by the global module fragment. The foreign-module body restriction then suppresses its available-external body, leaving a call that cannot be inlined and can fail to link. Allow GNU-inline definitions owned by global module fragments through that restriction while preserving their linkage and the remaining emission checks. Add regression coverage for both declaration orders at `-O0` and `-O2`, named-module ownership, and escaping function addresses, plus a release note. Testing: - Built assertion-enabled Clang 24 from upstream `f4bf388b08af`. - Confirmed the new regression fails before the fix and passes afterward. - Ran the complete `Modules`, `CodeGen`, and `CodeGenCXX` suites locally: 4,521 passed, 8 expected failures, 3,884 unsupported, and no unexpected failures. - Verified the original reproducer links in both declaration orders and both executables return 42. - Verified address-taking leaves the wrapper symbols undefined in object files at `-O0` and `-O2`. - Formatting and whitespace checks passed. Testing used an X86-only Linux build; tests requiring unavailable targets or platform features were skipped. Fixes #228800 >From 0354510b724d7a58e2b424f4079f0ec85154453b Mon Sep 17 00:00:00 2001 From: Mathew Joseph <[email protected]> Date: Mon, 5 Oct 2026 04:53:21 +0530 Subject: [PATCH] [Clang][Modules] Emit GMF GNU-inline bodies for inlining Importing a named module before including a GNU-inline wrapper header can retain the definition owned by the global module fragment. The foreign named-module body restriction then suppresses its available- external definition, leaving a call that cannot be inlined. Allow GNU-inline definitions owned by global module fragments through that restriction without changing their linkage or the remaining emission checks. Cover both declaration orders at O0 and O2, named- module ownership, and escaping function addresses. Fixes #228800 Assisted-by: GitHub Copilot --- clang/docs/ReleaseNotes.md | 4 + clang/lib/CodeGen/CodeGenModule.cpp | 3 +- .../gnu-inline-global-module-fragment.cppm | 75 +++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 clang/test/CodeGenCXX/gnu-inline-global-module-fragment.cppm diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 7ec126a065ae5..b76114c04d67c 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -645,6 +645,10 @@ features cannot lower the translation-unit ABI level; #### Bug Fixes to C++ Support +- Fixed an undefined reference to a GNU-inline function when its defining + header is included after importing a C++20 named module whose global module + fragment includes the same header. (#GH228800) + - Fixed lambdas with specifiers or attributes after the capture list being misparsed as function declarations in direct-initialization contexts under `-fms-extensions` or in HLSL mode. diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index b4ebc6b6abff0..fb43a103f9c92 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -5069,7 +5069,8 @@ bool CodeGenModule::shouldEmitFunction(GlobalDecl GD) { // 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 = F->getOwningModule(); - M && M->getTopLevelModule()->isNamedModule() && + M && !(M->isGlobalModule() && F->hasAttr<GNUInlineAttr>()) && + M->getTopLevelModule()->isNamedModule() && getContext().getCurrentNamedModule() != M->getTopLevelModule()) { // There are practices to mark template member function as always-inline // and mark the template as extern explicit instantiation but not give diff --git a/clang/test/CodeGenCXX/gnu-inline-global-module-fragment.cppm b/clang/test/CodeGenCXX/gnu-inline-global-module-fragment.cppm new file mode 100644 index 0000000000000..d44cba73b78e7 --- /dev/null +++ b/clang/test/CodeGenCXX/gnu-inline-global-module-fragment.cppm @@ -0,0 +1,75 @@ +// RUN: split-file %s %t +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/M.cppm -emit-module-interface -o %t/M.pcm +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t %t/use.cpp -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefixes=RAW,O0 +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t -DINCLUDE_FIRST %t/use.cpp -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefixes=RAW,O0 +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t -O2 %t/use.cpp -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefixes=RAW,O2 +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t -O2 -DINCLUDE_FIRST %t/use.cpp -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefixes=RAW,O2 +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t %t/use.cpp -emit-llvm -o - | FileCheck %s --check-prefix=INLINE --implicit-check-not=always_wrapper +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t -DINCLUDE_FIRST %t/use.cpp -emit-llvm -o - | FileCheck %s --check-prefix=INLINE --implicit-check-not=always_wrapper +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t -O2 %t/use.cpp -emit-llvm -o - | FileCheck %s --check-prefix=INLINE --implicit-check-not=always_wrapper +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t -O2 -DINCLUDE_FIRST %t/use.cpp -emit-llvm -o - | FileCheck %s --check-prefix=INLINE --implicit-check-not=always_wrapper +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t %t/address.cpp -emit-llvm -o - | FileCheck %s --check-prefix=ADDRESS +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t -DINCLUDE_FIRST %t/address.cpp -emit-llvm -o - | FileCheck %s --check-prefix=ADDRESS +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t -O2 %t/address.cpp -emit-llvm -o - | FileCheck %s --check-prefix=ADDRESS +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fprebuilt-module-path=%t -O2 -DINCLUDE_FIRST %t/address.cpp -emit-llvm -o - | FileCheck %s --check-prefix=ADDRESS + +// RAW-DAG: define available_externally{{.*}} @always_wrapper( +// RAW-DAG: attributes #{{[0-9]+}} = { alwaysinline +// RAW-DAG: declare{{.*}} @{{.*}}named_wrapper{{.*}}( +// O0-DAG: declare{{.*}} @plain_wrapper( +// O2-DAG: define available_externally{{.*}} @plain_wrapper( +// INLINE-LABEL: define{{.*}} @test_always( +// INLINE: call{{.*}} @real_fn( +// ADDRESS-LABEL: define{{.*}} @address_always( +// ADDRESS: ret ptr @always_wrapper +// ADDRESS-LABEL: define{{.*}} @address_plain( +// ADDRESS: ret ptr @plain_wrapper + +//--- wrappers.h +extern "C" { +long real_fn(long); + +extern inline __attribute__((gnu_inline, always_inline)) +long always_wrapper(long value) { return real_fn(value); } + +extern inline __attribute__((gnu_inline)) +long plain_wrapper(long value) { return real_fn(value); } +} + +//--- M.cppm +module; +#include "wrappers.h" +export module M; + +export extern inline __attribute__((gnu_inline, always_inline)) +long named_wrapper(long value) { return value + 1; } + +//--- use.cpp +#ifdef INCLUDE_FIRST +#include "wrappers.h" +import M; +#else +import M; +#include "wrappers.h" +#endif + +extern "C" long test_always() { return always_wrapper(41); } +extern "C" long test_plain() { return plain_wrapper(42); } +extern "C" long test_named() { return named_wrapper(43); } + +//--- address.cpp +#ifdef INCLUDE_FIRST +#include "wrappers.h" +import M; +#else +import M; +#include "wrappers.h" +#endif + +extern "C" auto address_always() -> long (*)(long) { + return &always_wrapper; +} + +extern "C" auto address_plain() -> long (*)(long) { + return &plain_wrapper; +} \ No newline at end of file _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
