https://github.com/cjc0013 updated 
https://github.com/llvm/llvm-project/pull/219238

>From 26800608844f90546741ee69c9b1461ac87aed59 Mon Sep 17 00:00:00 2001
From: cjc0013 <[email protected]>
Date: Thu, 27 Aug 2026 12:26:09 -0400
Subject: [PATCH 1/6] [clang] Disambiguate GMF internal functions across
 partitions

---
 clang/lib/AST/ItaniumMangle.cpp               | 27 ++++++++++-
 ...lobal-module-fragment-internal-linkage.cpp | 46 +++++++++++++++++++
 2 files changed, 72 insertions(+), 1 deletion(-)
 create mode 100644 
clang/test/Modules/global-module-fragment-internal-linkage.cpp

diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 7fb162a68fe88..b172f1a302f0a 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -1110,9 +1110,34 @@ void CXXNameMangler::mangleNameWithAbiTags(
 }
 
 void CXXNameMangler::mangleModuleName(const NamedDecl *ND) {
-  if (ND->isExternallyVisible())
+  if (ND->isExternallyVisible()) {
     if (Module *M = ND->getOwningModuleForLinkage())
       mangleModuleNamePrefix(M->getPrimaryModuleInterfaceName());
+    return;
+  }
+
+  // A function with internal linkage in a global module fragment denotes a
+  // different entity in every module unit. When definitions from multiple
+  // imported units are emitted into one translation unit, their ordinary
+  // internal-linkage names would otherwise collide.
+  const auto *FD = dyn_cast<FunctionDecl>(ND);
+  Module *M = ND->getOwningModule();
+  if (!FD || FD->getFormalLinkage() != Linkage::Internal || !M ||
+      !M->isGlobalModule() || !M->Parent ||
+      !M->Parent->isNamedModuleUnit())
+    return;
+
+  M = M->Parent;
+  if (!M->isModulePartition()) {
+    mangleModuleNamePrefix(M->Name);
+    return;
+  }
+
+  auto [PrimaryName, PartitionName] = StringRef(M->Name).rsplit(':');
+  assert(!PrimaryName.empty() && !PartitionName.empty() &&
+         "invalid module partition name");
+  mangleModuleNamePrefix(PrimaryName);
+  mangleModuleNamePrefix(PartitionName, /*IsPartition=*/true);
 }
 
 // <module-name> ::= <module-subname>
diff --git a/clang/test/Modules/global-module-fragment-internal-linkage.cpp 
b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
new file mode 100644
index 0000000000000..5298015d92abe
--- /dev/null
+++ b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
@@ -0,0 +1,46 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -emit-module-interface %t/part1.cppm -o %t/A-Part1.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -emit-module-interface %t/part2.cppm -o %t/A-Part2.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -emit-module-interface %t/A.cppm \
+// RUN:   -fmodule-file=%t/A-Part1.pcm -fmodule-file=%t/A-Part2.pcm \
+// RUN:   -o %t/A.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-llvm -o - \
+// RUN:   %t/use.cpp -fmodule-file=%t/A.pcm \
+// RUN:   -fmodule-file=%t/A-Part1.pcm -fmodule-file=%t/A-Part2.pcm \
+// RUN:   | FileCheck %s
+//
+// Two internal functions with the same ordinary mangled name must remain
+// distinct when their global module fragments are imported together.
+// CHECK-DAG: define internal {{.*}} @_ZLW1AWP5Part16helperv()
+// CHECK-DAG: define internal {{.*}} @_ZLW1AWP5Part26helperv()
+// CHECK-DAG: call {{.*}} @_ZLW1AWP5Part16helperv()
+// CHECK-DAG: call {{.*}} @_ZLW1AWP5Part26helperv()
+// CHECK-DAG: ret i32 1
+// CHECK-DAG: ret i32 2
+
+//--- part1.cppm
+module;
+static inline __attribute__((noinline)) int helper() { return 1; }
+export module A:Part1;
+export inline int part1() { return helper(); }
+
+//--- part2.cppm
+module;
+static inline __attribute__((noinline)) int helper() { return 2; }
+export module A:Part2;
+export inline int part2() { return helper(); }
+
+//--- A.cppm
+export module A;
+export import :Part1;
+export import :Part2;
+
+//--- use.cpp
+import A;
+int use() { return part1() + part2(); }

>From 0571efbe9495ecfc070b5aab9d75e9d8d0e9c034 Mon Sep 17 00:00:00 2001
From: cjc0013 <[email protected]>
Date: Thu, 27 Aug 2026 19:45:26 -0400
Subject: [PATCH 2/6] [clang] Distinguish GMF internal functions across module
 units

---
 clang/lib/Serialization/ASTReaderDecl.cpp     | 27 ++++++++++++++++---
 ...lobal-module-fragment-internal-linkage.cpp | 22 ++++++++-------
 2 files changed, 35 insertions(+), 14 deletions(-)

diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp 
b/clang/lib/Serialization/ASTReaderDecl.cpp
index 74e0106520011..0ff0472aacccb 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -3584,12 +3584,31 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
   }
 
   ASTContext &C = Reader.getContext();
+  auto IsSameEntity = [&](NamedDecl *Existing) {
+    if (!C.isSameEntity(Existing, D))
+      return false;
+
+    auto *FD = dyn_cast<FunctionDecl>(D);
+    auto *ExistingFD = dyn_cast<FunctionDecl>(Existing);
+    if (!FD || !ExistingFD ||
+        FD->getFormalLinkage() != Linkage::Internal ||
+        ExistingFD->getFormalLinkage() != Linkage::Internal)
+      return true;
+
+    Module *M = FD->getOwningModule();
+    Module *ExistingM = ExistingFD->getOwningModule();
+    if (!M || !ExistingM || !M->isGlobalModule() ||
+        !ExistingM->isGlobalModule())
+      return true;
+
+    return M->getTopLevelModule() == ExistingM->getTopLevelModule();
+  };
   DeclContext *DC = D->getDeclContext()->getRedeclContext();
   if (TypedefNameForLinkage) {
     auto It = Reader.ImportedTypedefNamesForLinkage.find(
         std::make_pair(DC, TypedefNameForLinkage));
     if (It != Reader.ImportedTypedefNamesForLinkage.end())
-      if (C.isSameEntity(It->second, D))
+      if (IsSameEntity(It->second))
         return FindExistingResult(Reader, D, It->second, AnonymousDeclNumber,
                                   TypedefNameForLinkage);
     // Go on to check in other places in case an existing typedef name
@@ -3601,7 +3620,7 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
     // in its context by number.
     if (auto *Existing = getAnonymousDeclForMerging(
             Reader, D->getLexicalDeclContext(), AnonymousDeclNumber))
-      if (C.isSameEntity(Existing, D))
+      if (IsSameEntity(Existing))
         return FindExistingResult(Reader, D, Existing, AnonymousDeclNumber,
                                   TypedefNameForLinkage);
   } else if (DC->isTranslationUnit() &&
@@ -3635,7 +3654,7 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
       if (NamedDecl *Existing =
               getDeclForMerging(*I, TypedefNameForLinkage,
                                 /*FilteringUsingShadowDecl=*/false))
-        if (C.isSameEntity(Existing, D))
+        if (IsSameEntity(Existing))
           return FindExistingResult(Reader, D, Existing, AnonymousDeclNumber,
                                     TypedefNameForLinkage);
     }
@@ -3644,7 +3663,7 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
     for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) 
{
       if (NamedDecl *Existing = getDeclForMerging(*I, TypedefNameForLinkage,
                                                   !isa<UsingShadowDecl>(D)))
-        if (C.isSameEntity(Existing, D)) {
+        if (IsSameEntity(Existing)) {
           return FindExistingResult(Reader, D, Existing, AnonymousDeclNumber,
                                     TypedefNameForLinkage);
         }
diff --git a/clang/test/Modules/global-module-fragment-internal-linkage.cpp 
b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
index 5298015d92abe..c9fa548ddf6dd 100644
--- a/clang/test/Modules/global-module-fragment-internal-linkage.cpp
+++ b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
@@ -8,21 +8,23 @@
 // RUN:   -emit-module-interface %t/part2.cppm -o %t/A-Part2.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
 // RUN:   -emit-module-interface %t/A.cppm \
-// RUN:   -fmodule-file=%t/A-Part1.pcm -fmodule-file=%t/A-Part2.pcm \
+// RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
+// RUN:   -fmodule-file=A:Part2=%t/A-Part2.pcm \
 // RUN:   -o %t/A.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-llvm -o - \
 // RUN:   %t/use.cpp -fmodule-file=%t/A.pcm \
-// RUN:   -fmodule-file=%t/A-Part1.pcm -fmodule-file=%t/A-Part2.pcm \
+// RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
+// RUN:   -fmodule-file=A:Part2=%t/A-Part2.pcm \
 // RUN:   | FileCheck %s
 //
-// Two internal functions with the same ordinary mangled name must remain
-// distinct when their global module fragments are imported together.
-// CHECK-DAG: define internal {{.*}} @_ZLW1AWP5Part16helperv()
-// CHECK-DAG: define internal {{.*}} @_ZLW1AWP5Part26helperv()
-// CHECK-DAG: call {{.*}} @_ZLW1AWP5Part16helperv()
-// CHECK-DAG: call {{.*}} @_ZLW1AWP5Part26helperv()
+// Identical internal functions from the same textual header must remain
+// distinct when separate global module fragments are imported together.
+// CHECK-DAG: define internal {{.*}} @_ZW1AWP5Part1L6helperv()
+// CHECK-DAG: define internal {{.*}} @_ZW1AWP5Part2L6helperv()
+// CHECK-DAG: call {{.*}} @_ZW1AWP5Part1L6helperv()
+// CHECK-DAG: call {{.*}} @_ZW1AWP5Part2L6helperv()
+// CHECK-DAG: ret i32 1
 // CHECK-DAG: ret i32 1
-// CHECK-DAG: ret i32 2
 
 //--- part1.cppm
 module;
@@ -32,7 +34,7 @@ export inline int part1() { return helper(); }
 
 //--- part2.cppm
 module;
-static inline __attribute__((noinline)) int helper() { return 2; }
+static inline __attribute__((noinline)) int helper() { return 1; }
 export module A:Part2;
 export inline int part2() { return helper(); }
 

>From 6e2137b2fa175dc25aa52ac635d260fa061870c3 Mon Sep 17 00:00:00 2001
From: cjc0013 <[email protected]>
Date: Mon, 31 Aug 2026 13:15:13 -0400
Subject: [PATCH 3/6] [clang] Address GMF disambiguation review feedback

---
 clang/docs/StandardCPlusPlusModules.md        |  7 +++++
 clang/lib/AST/ItaniumMangle.cpp               |  7 +++++
 clang/lib/Serialization/ASTReaderDecl.cpp     |  3 +++
 ...lobal-module-fragment-internal-linkage.cpp | 26 +++++++++++++++++++
 4 files changed, 43 insertions(+)

diff --git a/clang/docs/StandardCPlusPlusModules.md 
b/clang/docs/StandardCPlusPlusModules.md
index e9a1c332fe8a2..99df4b6416dc0 100644
--- a/clang/docs/StandardCPlusPlusModules.md
+++ b/clang/docs/StandardCPlusPlusModules.md
@@ -498,6 +498,13 @@ fragment is disabled by default. These checks can be 
enabled by specifying
 and you encounter incorrect or missing diagnostics, please report them via the
 [community issue tracker](https://github.com/llvm/llvm-project/issues/).
 
+When global module fragment ODR checking is skipped, Clang also keeps
+internal-linkage functions from different named module units distinct and
+includes the module-unit owner in their mangled names. This is a practical,
+non-conforming strategy for C-style `static inline` functions in real-world
+headers. `-Xclang -fno-skip-odr-check-in-gmf` restores the ordinary
+internal-linkage identity and mangling.
+
 ### Privacy Issue
 
 BMIs are not and should not be treated as an information hiding mechanism.
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index b172f1a302f0a..58fd1c339fc9b 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -1116,6 +1116,13 @@ void CXXNameMangler::mangleModuleName(const NamedDecl 
*ND) {
     return;
   }
 
+  // FIXME: Giving a TU-local entity a module-qualified name is not
+  // standard-conforming. This is Clang's practical strategy for real-world
+  // headers when GMF ODR checking is skipped; users can restore the ordinary
+  // internal-linkage mangling with -fno-skip-odr-check-in-gmf.
+  if (!getASTContext().getLangOpts().SkipODRCheckInGMF)
+    return;
+
   // A function with internal linkage in a global module fragment denotes a
   // different entity in every module unit. When definitions from multiple
   // imported units are emitted into one translation unit, their ordinary
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp 
b/clang/lib/Serialization/ASTReaderDecl.cpp
index 0ff0472aacccb..f1012c2cb0154 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -3588,6 +3588,9 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
     if (!C.isSameEntity(Existing, D))
       return false;
 
+    if (!C.getLangOpts().SkipODRCheckInGMF)
+      return true;
+
     auto *FD = dyn_cast<FunctionDecl>(D);
     auto *ExistingFD = dyn_cast<FunctionDecl>(Existing);
     if (!FD || !ExistingFD ||
diff --git a/clang/test/Modules/global-module-fragment-internal-linkage.cpp 
b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
index c9fa548ddf6dd..a06106a49b191 100644
--- a/clang/test/Modules/global-module-fragment-internal-linkage.cpp
+++ b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
@@ -3,20 +3,43 @@
 // RUN: split-file %s %t
 //
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -fskip-odr-check-in-gmf \
 // RUN:   -emit-module-interface %t/part1.cppm -o %t/A-Part1.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -fskip-odr-check-in-gmf \
 // RUN:   -emit-module-interface %t/part2.cppm -o %t/A-Part2.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -fskip-odr-check-in-gmf \
 // RUN:   -emit-module-interface %t/A.cppm \
 // RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
 // RUN:   -fmodule-file=A:Part2=%t/A-Part2.pcm \
 // RUN:   -o %t/A.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-llvm -o - \
+// RUN:   -fskip-odr-check-in-gmf \
 // RUN:   %t/use.cpp -fmodule-file=%t/A.pcm \
 // RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
 // RUN:   -fmodule-file=A:Part2=%t/A-Part2.pcm \
 // RUN:   | FileCheck %s
 //
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   -emit-module-interface %t/part1.cppm -o %t/no-A-Part1.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   -emit-module-interface %t/part2.cppm -o %t/no-A-Part2.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
+// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   -emit-module-interface %t/A.cppm \
+// RUN:   -fmodule-file=A:Part1=%t/no-A-Part1.pcm \
+// RUN:   -fmodule-file=A:Part2=%t/no-A-Part2.pcm \
+// RUN:   -o %t/no-A.pcm
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-llvm -o - \
+// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   %t/use.cpp -fmodule-file=%t/no-A.pcm \
+// RUN:   -fmodule-file=A:Part1=%t/no-A-Part1.pcm \
+// RUN:   -fmodule-file=A:Part2=%t/no-A-Part2.pcm \
+// RUN:   | FileCheck %s --check-prefix=NO-DISAMBIGUATION
+//
 // Identical internal functions from the same textual header must remain
 // distinct when separate global module fragments are imported together.
 // CHECK-DAG: define internal {{.*}} @_ZW1AWP5Part1L6helperv()
@@ -26,6 +49,9 @@
 // CHECK-DAG: ret i32 1
 // CHECK-DAG: ret i32 1
 
+// NO-DISAMBIGUATION: define internal {{.*}} @_ZL6helperv()
+// NO-DISAMBIGUATION-NOT: @_ZW1AWP5Part
+
 //--- part1.cppm
 module;
 static inline __attribute__((noinline)) int helper() { return 1; }

>From a7c1f633477822bf8b2e278a0ec0e66aa59ef755 Mon Sep 17 00:00:00 2001
From: cjc0013 <[email protected]>
Date: Tue, 1 Sep 2026 11:01:39 -0400
Subject: [PATCH 4/6] [clang] Separate GMF identity from ODR checking

---
 clang/docs/StandardCPlusPlusModules.md        | 15 ++++++------
 clang/include/clang/Basic/LangOptions.def     |  1 +
 clang/include/clang/Options/Options.td        | 11 +++++++++
 clang/lib/AST/ItaniumMangle.cpp               |  6 ++---
 clang/lib/Serialization/ASTReaderDecl.cpp     |  4 ++--
 ...lobal-module-fragment-internal-linkage.cpp | 24 ++++++++++++-------
 6 files changed, 41 insertions(+), 20 deletions(-)

diff --git a/clang/docs/StandardCPlusPlusModules.md 
b/clang/docs/StandardCPlusPlusModules.md
index 99df4b6416dc0..f3741e42a3955 100644
--- a/clang/docs/StandardCPlusPlusModules.md
+++ b/clang/docs/StandardCPlusPlusModules.md
@@ -498,13 +498,6 @@ fragment is disabled by default. These checks can be 
enabled by specifying
 and you encounter incorrect or missing diagnostics, please report them via the
 [community issue tracker](https://github.com/llvm/llvm-project/issues/).
 
-When global module fragment ODR checking is skipped, Clang also keeps
-internal-linkage functions from different named module units distinct and
-includes the module-unit owner in their mangled names. This is a practical,
-non-conforming strategy for C-style `static inline` functions in real-world
-headers. `-Xclang -fno-skip-odr-check-in-gmf` restores the ordinary
-internal-linkage identity and mangling.
-
 ### Privacy Issue
 
 BMIs are not and should not be treated as an information hiding mechanism.
@@ -521,6 +514,14 @@ the Itanium C++ ABI are covered.
 The declarations in a module unit which are not in the global module fragment
 have new linkage names.
 
+By default, Clang also keeps internal-linkage functions from global module
+fragments in different named module units distinct and includes the
+module-unit owner in their mangled names. This is a practical, non-conforming
+strategy for C-style `static inline` functions in real-world headers. Specify
+`-Xclang -fno-modules-unique-gmf-internal-linkage` to restore the ordinary
+internal-linkage identity and mangling. This behavior is independent of
+whether global module fragment ODR checking is enabled.
+
 For example,
 
 ```c++
diff --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index ad993ce7e5d95..a6db47518eb92 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -158,6 +158,7 @@ LANGOPT(Modules           , 1, 0, NotCompatible, "modules 
semantics")
 LANGOPT(ClangModules      , 1, 0, Compatible, "Clang header modules")
 LANGOPT(CPlusPlusModules  , 1, 0, Compatible, "C++ modules syntax")
 LANGOPT(SkipODRCheckInGMF , 1, 0, NotCompatible, "Skip ODR checks for decls in 
the global module fragment")
+LANGOPT(ModulesUniqueGMFInternalLinkage, 1, 1, NotCompatible, "Use unique 
internal linkage for functions in global module fragments")
 LANGOPT(BuiltinHeadersInSystemModules, 1, 0, NotCompatible, "builtin headers 
belong to system modules, and _Builtin_ modules are ignored for cstdlib 
headers")
 ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 3, CMK_None, Benign,
              "compiling a module interface")
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 3b88dce9c822b..b08e3099f8ccd 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3733,6 +3733,17 @@ defm skip_odr_check_in_gmf : BoolOption<"f", 
"skip-odr-check-in-gmf",
           "Perform ODR checks for decls in the global module fragment.">>,
   Group<f_Group>;
 
+defm modules_unique_gmf_internal_linkage : BoolOption<"f",
+  "modules-unique-gmf-internal-linkage",
+  LangOpts<"ModulesUniqueGMFInternalLinkage">, DefaultTrue,
+  PosFlag<SetTrue, [], [CC1Option],
+          "Use distinct internal-linkage identities for functions in global "
+          "module fragments.">,
+  NegFlag<SetFalse, [], [CC1Option],
+          "Use ordinary internal-linkage identities for functions in global "
+          "module fragments.">>,
+  Group<f_Group>;
+
 defm modules_reduced_bmi : BoolOption<"f", "modules-reduced-bmi",
   FrontendOpts<"GenReducedBMI">, DefaultFalse,
   NegFlag<SetFalse>,
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index fb725dac727ef..cea17f6b82b6d 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -1119,9 +1119,9 @@ void CXXNameMangler::mangleModuleName(const NamedDecl 
*ND) {
 
   // FIXME: Giving a TU-local entity a module-qualified name is not
   // standard-conforming. This is Clang's practical strategy for real-world
-  // headers when GMF ODR checking is skipped; users can restore the ordinary
-  // internal-linkage mangling with -fno-skip-odr-check-in-gmf.
-  if (!getASTContext().getLangOpts().SkipODRCheckInGMF)
+  // headers; users can restore the ordinary internal-linkage mangling with
+  // -fno-modules-unique-gmf-internal-linkage.
+  if (!getASTContext().getLangOpts().ModulesUniqueGMFInternalLinkage)
     return;
 
   // A function with internal linkage in a global module fragment denotes a
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp 
b/clang/lib/Serialization/ASTReaderDecl.cpp
index 82a2837e8b30d..9d05ebd465edd 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -3590,7 +3590,7 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
     if (!C.isSameEntity(Existing, D))
       return false;
 
-    if (!C.getLangOpts().SkipODRCheckInGMF)
+    if (!C.getLangOpts().ModulesUniqueGMFInternalLinkage)
       return true;
 
     auto *FD = dyn_cast<FunctionDecl>(D);
@@ -3606,7 +3606,7 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
         !ExistingM->isGlobalModule())
       return true;
 
-    return M->getTopLevelModule() == ExistingM->getTopLevelModule();
+    return false;
   };
   DeclContext *DC = D->getDeclContext()->getRedeclContext();
   if (TypedefNameForLinkage) {
diff --git a/clang/test/Modules/global-module-fragment-internal-linkage.cpp 
b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
index a06106a49b191..0c90f4c7288e2 100644
--- a/clang/test/Modules/global-module-fragment-internal-linkage.cpp
+++ b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
@@ -3,38 +3,46 @@
 // RUN: split-file %s %t
 //
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fskip-odr-check-in-gmf \
+// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   -fmodules-unique-gmf-internal-linkage \
 // RUN:   -emit-module-interface %t/part1.cppm -o %t/A-Part1.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fskip-odr-check-in-gmf \
+// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   -fmodules-unique-gmf-internal-linkage \
 // RUN:   -emit-module-interface %t/part2.cppm -o %t/A-Part2.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fskip-odr-check-in-gmf \
+// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   -fmodules-unique-gmf-internal-linkage \
 // RUN:   -emit-module-interface %t/A.cppm \
 // RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
 // RUN:   -fmodule-file=A:Part2=%t/A-Part2.pcm \
 // RUN:   -o %t/A.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-llvm -o - \
-// RUN:   -fskip-odr-check-in-gmf \
+// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   -fmodules-unique-gmf-internal-linkage \
 // RUN:   %t/use.cpp -fmodule-file=%t/A.pcm \
 // RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
 // RUN:   -fmodule-file=A:Part2=%t/A-Part2.pcm \
 // RUN:   | FileCheck %s
 //
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   -fskip-odr-check-in-gmf \
+// RUN:   -fno-modules-unique-gmf-internal-linkage \
 // RUN:   -emit-module-interface %t/part1.cppm -o %t/no-A-Part1.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   -fskip-odr-check-in-gmf \
+// RUN:   -fno-modules-unique-gmf-internal-linkage \
 // RUN:   -emit-module-interface %t/part2.cppm -o %t/no-A-Part2.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   -fskip-odr-check-in-gmf \
+// RUN:   -fno-modules-unique-gmf-internal-linkage \
 // RUN:   -emit-module-interface %t/A.cppm \
 // RUN:   -fmodule-file=A:Part1=%t/no-A-Part1.pcm \
 // RUN:   -fmodule-file=A:Part2=%t/no-A-Part2.pcm \
 // RUN:   -o %t/no-A.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-llvm -o - \
-// RUN:   -fno-skip-odr-check-in-gmf \
+// RUN:   -fskip-odr-check-in-gmf \
+// RUN:   -fno-modules-unique-gmf-internal-linkage \
 // RUN:   %t/use.cpp -fmodule-file=%t/no-A.pcm \
 // RUN:   -fmodule-file=A:Part1=%t/no-A-Part1.pcm \
 // RUN:   -fmodule-file=A:Part2=%t/no-A-Part2.pcm \

>From 2a5a09a91b815a1e65554ca2ac383ec2164fd7da Mon Sep 17 00:00:00 2001
From: cjc0013 <[email protected]>
Date: Tue, 1 Sep 2026 20:54:13 -0400
Subject: [PATCH 5/6] [clang] Use ODR hashes for GMF internal functions

---
 clang/docs/StandardCPlusPlusModules.md        |  8 --
 clang/include/clang/Basic/LangOptions.def     |  1 -
 clang/include/clang/Options/Options.td        | 11 ---
 clang/lib/AST/ItaniumMangle.cpp               | 34 +-------
 clang/lib/Serialization/ASTReaderDecl.cpp     | 13 +--
 ...lobal-module-fragment-internal-linkage.cpp | 83 ++++++++-----------
 6 files changed, 44 insertions(+), 106 deletions(-)

diff --git a/clang/docs/StandardCPlusPlusModules.md 
b/clang/docs/StandardCPlusPlusModules.md
index a9b0948b03c85..e56bdd23ad0d3 100644
--- a/clang/docs/StandardCPlusPlusModules.md
+++ b/clang/docs/StandardCPlusPlusModules.md
@@ -514,14 +514,6 @@ the Itanium C++ ABI are covered.
 The declarations in a module unit which are not in the global module fragment
 have new linkage names.
 
-By default, Clang also keeps internal-linkage functions from global module
-fragments in different named module units distinct and includes the
-module-unit owner in their mangled names. This is a practical, non-conforming
-strategy for C-style `static inline` functions in real-world headers. Specify
-`-Xclang -fno-modules-unique-gmf-internal-linkage` to restore the ordinary
-internal-linkage identity and mangling. This behavior is independent of
-whether global module fragment ODR checking is enabled.
-
 For example,
 
 ```c++
diff --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index a6db47518eb92..ad993ce7e5d95 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -158,7 +158,6 @@ LANGOPT(Modules           , 1, 0, NotCompatible, "modules 
semantics")
 LANGOPT(ClangModules      , 1, 0, Compatible, "Clang header modules")
 LANGOPT(CPlusPlusModules  , 1, 0, Compatible, "C++ modules syntax")
 LANGOPT(SkipODRCheckInGMF , 1, 0, NotCompatible, "Skip ODR checks for decls in 
the global module fragment")
-LANGOPT(ModulesUniqueGMFInternalLinkage, 1, 1, NotCompatible, "Use unique 
internal linkage for functions in global module fragments")
 LANGOPT(BuiltinHeadersInSystemModules, 1, 0, NotCompatible, "builtin headers 
belong to system modules, and _Builtin_ modules are ignored for cstdlib 
headers")
 ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 3, CMK_None, Benign,
              "compiling a module interface")
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index b08e3099f8ccd..3b88dce9c822b 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3733,17 +3733,6 @@ defm skip_odr_check_in_gmf : BoolOption<"f", 
"skip-odr-check-in-gmf",
           "Perform ODR checks for decls in the global module fragment.">>,
   Group<f_Group>;
 
-defm modules_unique_gmf_internal_linkage : BoolOption<"f",
-  "modules-unique-gmf-internal-linkage",
-  LangOpts<"ModulesUniqueGMFInternalLinkage">, DefaultTrue,
-  PosFlag<SetTrue, [], [CC1Option],
-          "Use distinct internal-linkage identities for functions in global "
-          "module fragments.">,
-  NegFlag<SetFalse, [], [CC1Option],
-          "Use ordinary internal-linkage identities for functions in global "
-          "module fragments.">>,
-  Group<f_Group>;
-
 defm modules_reduced_bmi : BoolOption<"f", "modules-reduced-bmi",
   FrontendOpts<"GenReducedBMI">, DefaultFalse,
   NegFlag<SetFalse>,
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 19589da392f46..3a3cde3448f44 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -1111,41 +1111,9 @@ void CXXNameMangler::mangleNameWithAbiTags(
 }
 
 void CXXNameMangler::mangleModuleName(const NamedDecl *ND) {
-  if (ND->isExternallyVisible()) {
+  if (ND->isExternallyVisible())
     if (Module *M = ND->getOwningModuleForLinkage())
       mangleModuleNamePrefix(M->getPrimaryModuleInterfaceName());
-    return;
-  }
-
-  // FIXME: Giving a TU-local entity a module-qualified name is not
-  // standard-conforming. This is Clang's practical strategy for real-world
-  // headers; users can restore the ordinary internal-linkage mangling with
-  // -fno-modules-unique-gmf-internal-linkage.
-  if (!getASTContext().getLangOpts().ModulesUniqueGMFInternalLinkage)
-    return;
-
-  // A function with internal linkage in a global module fragment denotes a
-  // different entity in every module unit. When definitions from multiple
-  // imported units are emitted into one translation unit, their ordinary
-  // internal-linkage names would otherwise collide.
-  const auto *FD = dyn_cast<FunctionDecl>(ND);
-  Module *M = ND->getOwningModule();
-  if (!FD || FD->getFormalLinkage() != Linkage::Internal || !M ||
-      !M->isGlobalModule() || !M->Parent ||
-      !M->Parent->isNamedModuleUnit())
-    return;
-
-  M = M->Parent;
-  if (!M->isModulePartition()) {
-    mangleModuleNamePrefix(M->Name);
-    return;
-  }
-
-  auto [PrimaryName, PartitionName] = StringRef(M->Name).rsplit(':');
-  assert(!PrimaryName.empty() && !PartitionName.empty() &&
-         "invalid module partition name");
-  mangleModuleNamePrefix(PrimaryName);
-  mangleModuleNamePrefix(PartitionName, /*IsPartition=*/true);
 }
 
 // <module-name> ::= <module-subname>
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp 
b/clang/lib/Serialization/ASTReaderDecl.cpp
index 9d05ebd465edd..929516535adc2 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -3590,13 +3590,9 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
     if (!C.isSameEntity(Existing, D))
       return false;
 
-    if (!C.getLangOpts().ModulesUniqueGMFInternalLinkage)
-      return true;
-
     auto *FD = dyn_cast<FunctionDecl>(D);
     auto *ExistingFD = dyn_cast<FunctionDecl>(Existing);
-    if (!FD || !ExistingFD ||
-        FD->getFormalLinkage() != Linkage::Internal ||
+    if (!FD || !ExistingFD || FD->getFormalLinkage() != Linkage::Internal ||
         ExistingFD->getFormalLinkage() != Linkage::Internal)
       return true;
 
@@ -3606,6 +3602,13 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
         !ExistingM->isGlobalModule())
       return true;
 
+    // Keep ordinary internal-linkage names. Identical definitions can share
+    // one entity. Diagnose non-equivalent definitions through the usual
+    // function ODR path after their lazy bodies have been loaded.
+    if (FD->getODRHash() == ExistingFD->getODRHash())
+      return true;
+
+    Reader.PendingFunctionOdrMergeFailures[FD].push_back(ExistingFD);
     return false;
   };
   DeclContext *DC = D->getDeclContext()->getRedeclContext();
diff --git a/clang/test/Modules/global-module-fragment-internal-linkage.cpp 
b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
index 0c90f4c7288e2..469c30013098a 100644
--- a/clang/test/Modules/global-module-fragment-internal-linkage.cpp
+++ b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
@@ -3,72 +3,59 @@
 // RUN: split-file %s %t
 //
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fno-skip-odr-check-in-gmf \
-// RUN:   -fmodules-unique-gmf-internal-linkage \
-// RUN:   -emit-module-interface %t/part1.cppm -o %t/A-Part1.pcm
+// RUN:   -fskip-odr-check-in-gmf -emit-module-interface %t/part1.cppm \
+// RUN:   -o %t/A-Part1.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fno-skip-odr-check-in-gmf \
-// RUN:   -fmodules-unique-gmf-internal-linkage \
-// RUN:   -emit-module-interface %t/part2.cppm -o %t/A-Part2.pcm
+// RUN:   -fskip-odr-check-in-gmf -emit-module-interface %t/part2.cppm \
+// RUN:   -o %t/A-Part2.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fno-skip-odr-check-in-gmf \
-// RUN:   -fmodules-unique-gmf-internal-linkage \
-// RUN:   -emit-module-interface %t/A.cppm \
+// RUN:   -fskip-odr-check-in-gmf -emit-module-interface %t/A.cppm \
 // RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
-// RUN:   -fmodule-file=A:Part2=%t/A-Part2.pcm \
-// RUN:   -o %t/A.pcm
+// RUN:   -fmodule-file=A:Part2=%t/A-Part2.pcm -o %t/A.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-llvm -o - \
-// RUN:   -fno-skip-odr-check-in-gmf \
-// RUN:   -fmodules-unique-gmf-internal-linkage \
-// RUN:   %t/use.cpp -fmodule-file=%t/A.pcm \
+// RUN:   -fskip-odr-check-in-gmf %t/use.cpp -fmodule-file=%t/A.pcm \
 // RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
-// RUN:   -fmodule-file=A:Part2=%t/A-Part2.pcm \
-// RUN:   | FileCheck %s
+// RUN:   -fmodule-file=A:Part2=%t/A-Part2.pcm | FileCheck %s 
--check-prefix=SAME
 //
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -DRETURN_VALUE=2 \
+// RUN:   -fskip-odr-check-in-gmf -emit-module-interface %t/part2.cppm \
+// RUN:   -o %t/different-A-Part2.pcm
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fskip-odr-check-in-gmf \
-// RUN:   -fno-modules-unique-gmf-internal-linkage \
-// RUN:   -emit-module-interface %t/part1.cppm -o %t/no-A-Part1.pcm
-// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fskip-odr-check-in-gmf \
-// RUN:   -fno-modules-unique-gmf-internal-linkage \
-// RUN:   -emit-module-interface %t/part2.cppm -o %t/no-A-Part2.pcm
-// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
-// RUN:   -fskip-odr-check-in-gmf \
-// RUN:   -fno-modules-unique-gmf-internal-linkage \
-// RUN:   -emit-module-interface %t/A.cppm \
-// RUN:   -fmodule-file=A:Part1=%t/no-A-Part1.pcm \
-// RUN:   -fmodule-file=A:Part2=%t/no-A-Part2.pcm \
-// RUN:   -o %t/no-A.pcm
-// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-llvm -o - \
-// RUN:   -fskip-odr-check-in-gmf \
-// RUN:   -fno-modules-unique-gmf-internal-linkage \
-// RUN:   %t/use.cpp -fmodule-file=%t/no-A.pcm \
-// RUN:   -fmodule-file=A:Part1=%t/no-A-Part1.pcm \
-// RUN:   -fmodule-file=A:Part2=%t/no-A-Part2.pcm \
-// RUN:   | FileCheck %s --check-prefix=NO-DISAMBIGUATION
+// RUN:   -fskip-odr-check-in-gmf -emit-module-interface %t/A.cppm \
+// RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
+// RUN:   -fmodule-file=A:Part2=%t/different-A-Part2.pcm \
+// RUN:   -o %t/different-A.pcm
+// RUN: not %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-llvm -o - \
+// RUN:   -fskip-odr-check-in-gmf %t/use.cpp -fmodule-file=%t/different-A.pcm \
+// RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
+// RUN:   -fmodule-file=A:Part2=%t/different-A-Part2.pcm 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=DIFFERENT
 //
-// Identical internal functions from the same textual header must remain
-// distinct when separate global module fragments are imported together.
-// CHECK-DAG: define internal {{.*}} @_ZW1AWP5Part1L6helperv()
-// CHECK-DAG: define internal {{.*}} @_ZW1AWP5Part2L6helperv()
-// CHECK-DAG: call {{.*}} @_ZW1AWP5Part1L6helperv()
-// CHECK-DAG: call {{.*}} @_ZW1AWP5Part2L6helperv()
-// CHECK-DAG: ret i32 1
-// CHECK-DAG: ret i32 1
+// Equivalent definitions are merged and retain the ordinary internal-linkage
+// name.
+// SAME-COUNT-2: call {{.*}} @_ZL6helperv()
+// SAME-COUNT-1: define internal {{.*}} @_ZL6helperv()
 
-// NO-DISAMBIGUATION: define internal {{.*}} @_ZL6helperv()
-// NO-DISAMBIGUATION-NOT: @_ZW1AWP5Part
+// Non-equivalent definitions remain distinct and are diagnosed through the
+// function ODR path, even when general GMF ODR checking is skipped.
+// DIFFERENT: error: 'helper' has different definitions in different modules
 
 //--- part1.cppm
 module;
+extern "C" {
 static inline __attribute__((noinline)) int helper() { return 1; }
+}
 export module A:Part1;
 export inline int part1() { return helper(); }
 
 //--- part2.cppm
 module;
-static inline __attribute__((noinline)) int helper() { return 1; }
+#ifndef RETURN_VALUE
+#define RETURN_VALUE 1
+#endif
+extern "C" {
+static inline __attribute__((noinline)) int helper() { return RETURN_VALUE; }
+}
 export module A:Part2;
 export inline int part2() { return helper(); }
 

>From c687ed74905d98f6ef9b8b65ba49a436f3444aaa Mon Sep 17 00:00:00 2001
From: cjc0013 <[email protected]>
Date: Wed, 2 Sep 2026 10:50:57 -0400
Subject: [PATCH 6/6] [clang] Preserve distinct GMF internal functions

---
 clang/lib/AST/ASTContext.cpp                  |  9 +++++
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        | 21 ++++++++++++
 clang/lib/CodeGen/CodeGenModule.cpp           | 21 ++++++++++++
 clang/lib/Serialization/ASTReaderDecl.cpp     | 33 +++----------------
 ...lobal-module-fragment-internal-linkage.cpp | 26 ++++++++++-----
 5 files changed, 72 insertions(+), 38 deletions(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index b7e771595e86e..6a7f84b02fe75 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -7793,6 +7793,15 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const 
NamedDecl *Y) const {
   // functions, etc.
   if (const auto *FuncX = dyn_cast<FunctionDecl>(X)) {
     const auto *FuncY = cast<FunctionDecl>(Y);
+
+    // Internal-linkage functions in different global module fragments denote
+    // different entities, even if they otherwise have the same name and type.
+    if (FuncX->getFormalLinkage() == Linkage::Internal &&
+        FuncY->getFormalLinkage() == Linkage::Internal &&
+        FuncX->isFromGlobalModule() && FuncY->isFromGlobalModule() &&
+        FuncX->getOwningModule() != FuncY->getOwningModule())
+      return false;
+
     if (const auto *CtorX = dyn_cast<CXXConstructorDecl>(X)) {
       const auto *CtorY = cast<CXXConstructorDecl>(Y);
       if (CtorX->getInheritedConstructor() &&
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 6564c00e4dd3a..3064f5b39a949 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2755,6 +2755,18 @@ static std::string getMangledNameImpl(CIRGenModule &cgm, 
GlobalDecl gd,
   return std::string(out.str());
 }
 
+static bool areDistinctInternalGlobalModuleEntities(GlobalDecl gd,
+                                                    GlobalDecl otherGd) {
+  const auto *fd = dyn_cast_or_null<FunctionDecl>(gd.getDecl());
+  const auto *otherFd = dyn_cast_or_null<FunctionDecl>(otherGd.getDecl());
+  if (!fd || !otherFd || fd->getFormalLinkage() != Linkage::Internal ||
+      otherFd->getFormalLinkage() != Linkage::Internal ||
+      !fd->isFromGlobalModule() || !otherFd->isFromGlobalModule())
+    return false;
+
+  return fd->getOwningModule() != otherFd->getOwningModule();
+}
+
 static FunctionDecl *
 createOpenACCBindTempFunction(ASTContext &ctx, const IdentifierInfo *bindName,
                               const FunctionDecl *protoFunc) {
@@ -2848,6 +2860,15 @@ StringRef CIRGenModule::getMangledName(GlobalDecl gd) {
   std::string mangledName = getMangledNameImpl(*this, gd, nd);
 
   auto result = manglings.insert(std::make_pair(mangledName, gd));
+  if (!result.second &&
+      areDistinctInternalGlobalModuleEntities(gd, result.first->second)) {
+    unsigned discriminator = 1;
+    do {
+      std::string uniqueName =
+          (Twine(mangledName) + "." + Twine(discriminator++)).str();
+      result = manglings.insert(std::make_pair(uniqueName, gd));
+    } while (!result.second);
+  }
   return mangledDeclNames[canonicalGd] = result.first->first();
 }
 
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 17b5f0fe4133d..52d79d9d9a3f5 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2474,6 +2474,18 @@ static bool isUniqueInternalLinkageDecl(GlobalDecl GD,
          (CGM.getFunctionLinkage(GD) == llvm::GlobalValue::InternalLinkage);
 }
 
+static bool areDistinctInternalGlobalModuleEntities(GlobalDecl GD,
+                                                    GlobalDecl OtherGD) {
+  const auto *FD = dyn_cast_or_null<FunctionDecl>(GD.getDecl());
+  const auto *OtherFD = dyn_cast_or_null<FunctionDecl>(OtherGD.getDecl());
+  if (!FD || !OtherFD || FD->getFormalLinkage() != Linkage::Internal ||
+      OtherFD->getFormalLinkage() != Linkage::Internal ||
+      !FD->isFromGlobalModule() || !OtherFD->isFromGlobalModule())
+    return false;
+
+  return FD->getOwningModule() != OtherFD->getOwningModule();
+}
+
 static std::string getMangledNameImpl(CodeGenModule &CGM, GlobalDecl GD,
                                       const NamedDecl *ND,
                                       bool OmitMultiVersionMangling = false) {
@@ -2675,6 +2687,15 @@ StringRef CodeGenModule::getMangledName(GlobalDecl GD) {
   //        "LLVM demangler must demangle clang-generated names");
 
   auto Result = Manglings.insert(std::make_pair(MangledName, GD));
+  if (!Result.second &&
+      areDistinctInternalGlobalModuleEntities(GD, Result.first->second)) {
+    unsigned Discriminator = 1;
+    do {
+      std::string UniqueName =
+          (Twine(MangledName) + "." + Twine(Discriminator++)).str();
+      Result = Manglings.insert(std::make_pair(UniqueName, GD));
+    } while (!Result.second);
+  }
   return MangledDeclNames[CanonicalGD] = Result.first->first();
 }
 
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp 
b/clang/lib/Serialization/ASTReaderDecl.cpp
index 929516535adc2..d4c47a81ed32f 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -3586,37 +3586,12 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
   }
 
   ASTContext &C = Reader.getContext();
-  auto IsSameEntity = [&](NamedDecl *Existing) {
-    if (!C.isSameEntity(Existing, D))
-      return false;
-
-    auto *FD = dyn_cast<FunctionDecl>(D);
-    auto *ExistingFD = dyn_cast<FunctionDecl>(Existing);
-    if (!FD || !ExistingFD || FD->getFormalLinkage() != Linkage::Internal ||
-        ExistingFD->getFormalLinkage() != Linkage::Internal)
-      return true;
-
-    Module *M = FD->getOwningModule();
-    Module *ExistingM = ExistingFD->getOwningModule();
-    if (!M || !ExistingM || !M->isGlobalModule() ||
-        !ExistingM->isGlobalModule())
-      return true;
-
-    // Keep ordinary internal-linkage names. Identical definitions can share
-    // one entity. Diagnose non-equivalent definitions through the usual
-    // function ODR path after their lazy bodies have been loaded.
-    if (FD->getODRHash() == ExistingFD->getODRHash())
-      return true;
-
-    Reader.PendingFunctionOdrMergeFailures[FD].push_back(ExistingFD);
-    return false;
-  };
   DeclContext *DC = D->getDeclContext()->getRedeclContext();
   if (TypedefNameForLinkage) {
     auto It = Reader.ImportedTypedefNamesForLinkage.find(
         std::make_pair(DC, TypedefNameForLinkage));
     if (It != Reader.ImportedTypedefNamesForLinkage.end())
-      if (IsSameEntity(It->second))
+      if (C.isSameEntity(It->second, D))
         return FindExistingResult(Reader, D, It->second, AnonymousDeclNumber,
                                   TypedefNameForLinkage);
     // Go on to check in other places in case an existing typedef name
@@ -3628,7 +3603,7 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
     // in its context by number.
     if (auto *Existing = getAnonymousDeclForMerging(
             Reader, D->getLexicalDeclContext(), AnonymousDeclNumber))
-      if (IsSameEntity(Existing))
+      if (C.isSameEntity(Existing, D))
         return FindExistingResult(Reader, D, Existing, AnonymousDeclNumber,
                                   TypedefNameForLinkage);
   } else if (DC->isTranslationUnit() &&
@@ -3662,7 +3637,7 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
       if (NamedDecl *Existing =
               getDeclForMerging(*I, TypedefNameForLinkage,
                                 /*FilteringUsingShadowDecl=*/false))
-        if (IsSameEntity(Existing))
+        if (C.isSameEntity(Existing, D))
           return FindExistingResult(Reader, D, Existing, AnonymousDeclNumber,
                                     TypedefNameForLinkage);
     }
@@ -3671,7 +3646,7 @@ ASTDeclReader::FindExistingResult 
ASTDeclReader::findExisting(NamedDecl *D) {
     for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) 
{
       if (NamedDecl *Existing = getDeclForMerging(*I, TypedefNameForLinkage,
                                                   !isa<UsingShadowDecl>(D)))
-        if (IsSameEntity(Existing)) {
+        if (C.isSameEntity(Existing, D)) {
           return FindExistingResult(Reader, D, Existing, AnonymousDeclNumber,
                                     TypedefNameForLinkage);
         }
diff --git a/clang/test/Modules/global-module-fragment-internal-linkage.cpp 
b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
index 469c30013098a..14712e5402bce 100644
--- a/clang/test/Modules/global-module-fragment-internal-linkage.cpp
+++ b/clang/test/Modules/global-module-fragment-internal-linkage.cpp
@@ -25,20 +25,28 @@
 // RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
 // RUN:   -fmodule-file=A:Part2=%t/different-A-Part2.pcm \
 // RUN:   -o %t/different-A.pcm
-// RUN: not %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-llvm -o - \
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-llvm -o - \
 // RUN:   -fskip-odr-check-in-gmf %t/use.cpp -fmodule-file=%t/different-A.pcm \
 // RUN:   -fmodule-file=A:Part1=%t/A-Part1.pcm \
-// RUN:   -fmodule-file=A:Part2=%t/different-A-Part2.pcm 2>&1 \
+// RUN:   -fmodule-file=A:Part2=%t/different-A-Part2.pcm \
 // RUN:   | FileCheck %s --check-prefix=DIFFERENT
 //
-// Equivalent definitions are merged and retain the ordinary internal-linkage
-// name.
-// SAME-COUNT-2: call {{.*}} @_ZL6helperv()
-// SAME-COUNT-1: define internal {{.*}} @_ZL6helperv()
+// Internal-linkage functions from different global module fragments remain
+// distinct entities even when their definitions are equivalent. CodeGen keeps
+// the ordinary mangled name and uniquifies only the colliding IR name.
+// SAME-DAG: call {{.*}} @_ZL6helperv()
+// SAME-DAG: call {{.*}} @_ZL6helperv.[[SAME_SUFFIX:[0-9]+]]()
+// SAME-DAG: define internal {{.*}} @_ZL6helperv()
+// SAME-DAG: define internal {{.*}} @_ZL6helperv.[[SAME_SUFFIX]]()
 
-// Non-equivalent definitions remain distinct and are diagnosed through the
-// function ODR path, even when general GMF ODR checking is skipped.
-// DIFFERENT: error: 'helper' has different definitions in different modules
+// Different definitions also remain distinct and retain their respective
+// bodies, even when general GMF ODR checking is skipped.
+// DIFFERENT-DAG: call {{.*}} @_ZL6helperv()
+// DIFFERENT-DAG: call {{.*}} @_ZL6helperv.[[DIFFERENT_SUFFIX:[0-9]+]]()
+// DIFFERENT-DAG: define internal {{.*}} @_ZL6helperv()
+// DIFFERENT-DAG: define internal {{.*}} @_ZL6helperv.[[DIFFERENT_SUFFIX]]()
+// DIFFERENT-DAG: ret i32 1
+// DIFFERENT-DAG: ret i32 2
 
 //--- part1.cppm
 module;

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

Reply via email to