[clang] [llvm] [MergeFuncs] Don't introduce calls to (linkonce,weak)_odr functions. (PR #125050)

Florian Hahn via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 25 06:05:17 PST 2025


https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/125050

>From 3133b6bf495e82ec63a29ab639ab91ddd7421464 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 30 Jan 2025 11:33:12 +0000
Subject: [PATCH 1/7] [MergeFuncs] Don't introduce calls to weak_odr functions.

Avoid creating new calls to weak_odr functions when merging 2 functions.

Consider 2 functions below, both present in 2 modules. Without this
patch, MergeFuncs in the first module may optimize A to call B and in
the second module B to call A.

Note that the 2 optimizations are vaild in isolation, but the linker then
could pick A from module 1 (which calls B) and B from module 2 which
calls A, introducing an infinte call cycle.

There may be other linkage types we need to be more careful about as
well.

    define weak_odr hidden void @"A"(ptr %p) {
    entry:
      tail call void @"foo"(ptr %p)
      ret void
    }

    define weak_odr hidden void @"B"(ptr %p) {
    entry:
      tail call void @"foo"(ptr %p)
      ret void
    }
---
 llvm/lib/Transforms/IPO/MergeFunctions.cpp    |  4 +-
 .../Transforms/MergeFunc/merge-weak-odr.ll    | 61 -------------------
 2 files changed, 2 insertions(+), 63 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index e8508416f5427..d991ec64445ee 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -891,8 +891,8 @@ bool MergeFunctions::writeThunkOrAlias(Function *F, Function *G) {
 
 // Merge two equivalent functions. Upon completion, Function G is deleted.
 void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
-  if (F->isInterposable()) {
-    assert(G->isInterposable());
+  if (F->isInterposable() || G->hasWeakODRLinkage()) {
+    assert(G->isInterposable() || G->hasWeakODRLinkage());
 
     // Both writeThunkOrAlias() calls below must succeed, either because we can
     // create aliases for G and NewF, or because a thunk for F is profitable.
diff --git a/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll b/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll
index 3ea279a411c72..ef011cbb1bde0 100644
--- a/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll
+++ b/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll
@@ -85,66 +85,5 @@ entry:
 }
 
 declare void @zar(ptr)
-; CHECK-LABEL: define void @caller_of_callers(
-; CHECK-SAME: ptr [[P:%.*]]) {
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_bar_2(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_bar_2(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_bar_2(ptr [[P]])
-; CHECK-NEXT:    call void @hidden_caller_of_zar_1(ptr [[P]])
-; CHECK-NEXT:    call void @hidden_caller_of_zar_1(ptr [[P]])
-; CHECK-NEXT:    ret void
-;
-;
-; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_foo_1(
-; CHECK-SAME: ptr [[P:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    tail call void @foo(ptr [[P]])
-; CHECK-NEXT:    tail call void @foo(ptr [[P]])
-; CHECK-NEXT:    tail call void @foo(ptr [[P]])
-; CHECK-NEXT:    ret void
-;
-;
-; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_bar_2(
-; CHECK-SAME: ptr [[P:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    tail call void @bar(ptr [[P]])
-; CHECK-NEXT:    tail call void @bar(ptr [[P]])
-; CHECK-NEXT:    tail call void @bar(ptr [[P]])
-; CHECK-NEXT:    ret void
-;
-;
-; CHECK-LABEL: define hidden void @hidden_caller_of_zar_1(
-; CHECK-SAME: ptr [[P:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    tail call void @zar(ptr [[P]])
-; CHECK-NEXT:    tail call void @zar(ptr [[P]])
-; CHECK-NEXT:    tail call void @zar(ptr [[P]])
-; CHECK-NEXT:    ret void
-;
-;
-; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_foo_2(
-; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_1(ptr [[TMP0]])
-; CHECK-NEXT:    ret void
-;
-;
-; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_foo_3(
-; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_1(ptr [[TMP0]])
-; CHECK-NEXT:    ret void
-;
-;
-; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_bar_3(
-; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_bar_2(ptr [[TMP0]])
-; CHECK-NEXT:    ret void
-;
-;
-; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_zar_2(
-; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @hidden_caller_of_zar_1(ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;

>From e5c826f79cb385ccc6c7e54fc9665dad35af94a9 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 30 Jan 2025 16:06:02 +0000
Subject: [PATCH 2/7] !fixup also include linkonce_odr

---
 llvm/lib/Transforms/IPO/MergeFunctions.cpp | 4 ++--
 llvm/test/Transforms/MergeFunc/comdat.ll   | 5 +++--
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index d991ec64445ee..ea5e102156ff7 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -891,8 +891,8 @@ bool MergeFunctions::writeThunkOrAlias(Function *F, Function *G) {
 
 // Merge two equivalent functions. Upon completion, Function G is deleted.
 void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
-  if (F->isInterposable() || G->hasWeakODRLinkage()) {
-    assert(G->isInterposable() || G->hasWeakODRLinkage());
+  if (F->isInterposable() || G->hasWeakODRLinkage() || G->hasLinkOnceODRLinkage()) {
+    assert(G->isInterposable() || G->hasWeakODRLinkage()|| G->hasLinkOnceODRLinkage());
 
     // Both writeThunkOrAlias() calls below must succeed, either because we can
     // create aliases for G and NewF, or because a thunk for F is profitable.
diff --git a/llvm/test/Transforms/MergeFunc/comdat.ll b/llvm/test/Transforms/MergeFunc/comdat.ll
index f6e104625bc41..3770c772b3f88 100644
--- a/llvm/test/Transforms/MergeFunc/comdat.ll
+++ b/llvm/test/Transforms/MergeFunc/comdat.ll
@@ -19,6 +19,7 @@ define linkonce_odr hidden i32 @g(i32 %x, i32 %y) comdat {
   ret i32 %sum3
 }
 
-; CHECK-DAG: define linkonce_odr hidden i32 @f(i32 %x, i32 %y) comdat
-; CHECK-DAG: define linkonce_odr hidden i32 @g(i32 %0, i32 %1) comdat
+; CHECK-DAG: define private i32 @0(i32 %x, i32 %y) comdat($f)
+; CHECK-DAG: define linkonce_odr hidden i32 @g(i32 %0, i32 %1) comdat {
+; CHECK-DAG: define linkonce_odr hidden i32 @f(i32 %0, i32 %1) {
 

>From d19f98db04131b8b1e1abfcaff0b5b442076bf7c Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 30 Jan 2025 16:16:08 +0000
Subject: [PATCH 3/7] !fixup fix formatting

---
 llvm/lib/Transforms/IPO/MergeFunctions.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index ea5e102156ff7..7130e8ce04817 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -891,8 +891,10 @@ bool MergeFunctions::writeThunkOrAlias(Function *F, Function *G) {
 
 // Merge two equivalent functions. Upon completion, Function G is deleted.
 void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
-  if (F->isInterposable() || G->hasWeakODRLinkage() || G->hasLinkOnceODRLinkage()) {
-    assert(G->isInterposable() || G->hasWeakODRLinkage()|| G->hasLinkOnceODRLinkage());
+  if (F->isInterposable() || G->hasWeakODRLinkage() ||
+      G->hasLinkOnceODRLinkage()) {
+    assert(G->isInterposable() || G->hasWeakODRLinkage() ||
+           G->hasLinkOnceODRLinkage());
 
     // Both writeThunkOrAlias() calls below must succeed, either because we can
     // create aliases for G and NewF, or because a thunk for F is profitable.

>From 8e006cfb3934713fbc19a0a14260a45ac1375628 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sun, 23 Feb 2025 08:57:17 +0000
Subject: [PATCH 4/7] !fixup Document and sort by weak/linkince ODR.

---
 llvm/lib/Transforms/IPO/MergeFunctions.cpp    | 13 ++++
 .../test/Transforms/MergeFunc/linkonce_odr.ll |  6 ++
 .../MergeFunc/merge-linkonce-odr-used.ll      | 12 ++--
 .../merge-linkonce-odr-weak-odr-mixed-used.ll | 14 ++--
 .../MergeFunc/merge-linkonce-odr.ll           | 46 ++++++++++---
 .../MergeFunc/merge-weak-odr-used.ll          | 12 ++--
 .../Transforms/MergeFunc/merge-weak-odr.ll    | 67 +++++++++++++++++++
 7 files changed, 143 insertions(+), 27 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index 7130e8ce04817..11720d65a72a2 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -891,6 +891,10 @@ bool MergeFunctions::writeThunkOrAlias(Function *F, Function *G) {
 
 // Merge two equivalent functions. Upon completion, Function G is deleted.
 void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
+
+  // Create a new thunk that both F and G can call, if F cannot call G directly.
+  // That is the case if F is either interposable or if G is either weak_odr or
+  // linkonce_odr.
   if (F->isInterposable() || G->hasWeakODRLinkage() ||
       G->hasLinkOnceODRLinkage()) {
     assert(G->isInterposable() || G->hasWeakODRLinkage() ||
@@ -993,11 +997,20 @@ static bool isFuncOrderCorrect(const Function *F, const Function *G) {
     // one, but not the other way around.
     return !F->isInterposable();
   }
+
+  if (F->hasWeakODRLinkage() != G->hasWeakODRLinkage() ||
+      F->hasLinkOnceODRLinkage() != G->hasLinkOnceODRLinkage()) {
+    // ODR functions before non-ODR functions. A ODR function can call a non-ODR
+    // function if it is not interposable, but not the other way around.
+    return F->hasWeakODRLinkage() || F->hasLinkOnceODRLinkage();
+  }
+
   if (F->hasLocalLinkage() != G->hasLocalLinkage()) {
     // External before local, because we definitely have to keep the external
     // function, but may be able to drop the local one.
     return !F->hasLocalLinkage();
   }
+
   // Impose a total order (by name) on the replacement of functions. This is
   // important when operating on more than one module independently to prevent
   // cycles of thunks calling each other when the modules are linked together.
diff --git a/llvm/test/Transforms/MergeFunc/linkonce_odr.ll b/llvm/test/Transforms/MergeFunc/linkonce_odr.ll
index 14b56a8ece30e..1a0e578f96790 100644
--- a/llvm/test/Transforms/MergeFunc/linkonce_odr.ll
+++ b/llvm/test/Transforms/MergeFunc/linkonce_odr.ll
@@ -45,6 +45,12 @@ define linkonce_odr i32 @funA(i32 %x, i32 %y) {
 ; CHECK-NEXT:    ret i32 [[SUM3]]
 ;
 ;
+; CHECK-LABEL: define linkonce_odr i32 @funC(
+; CHECK-SAME: i32 [[TMP0:%.*]], i32 [[TMP1:%.*]]) {
+; CHECK-NEXT:    [[TMP3:%.*]] = tail call i32 @funA(i32 [[TMP0]], i32 [[TMP1]])
+; CHECK-NEXT:    ret i32 [[TMP3]]
+;
+;
 ; CHECK-LABEL: define linkonce_odr i32 @funB(
 ; CHECK-SAME: i32 [[TMP0:%.*]], i32 [[TMP1:%.*]]) {
 ; CHECK-NEXT:    [[TMP3:%.*]] = tail call i32 @funA(i32 [[TMP0]], i32 [[TMP1]])
diff --git a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-used.ll b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-used.ll
index fe6a5210454b6..31b6aaffe9f63 100644
--- a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-used.ll
+++ b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-used.ll
@@ -42,12 +42,12 @@ declare void @foo(ptr)
 ; CHECK-LABEL: define void @caller_of_callers(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_1(ptr [[P]])
+; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_2(ptr [[P]])
+; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_3(ptr [[P]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_1(
+; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_3(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @foo(ptr [[P]])
@@ -58,12 +58,12 @@ declare void @foo(ptr)
 ;
 ; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_2(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_1(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_3(ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_3(
+; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_1(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_1(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_3(ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
diff --git a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-weak-odr-mixed-used.ll b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-weak-odr-mixed-used.ll
index 63c6b22424f4c..ee67dfb9be437 100644
--- a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-weak-odr-mixed-used.ll
+++ b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-weak-odr-mixed-used.ll
@@ -41,13 +41,13 @@ declare void @foo(ptr)
 ;.
 ; CHECK-LABEL: define void @caller_of_callers(
 ; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @weak_odr_caller_of_foo_1(ptr [[P]])
 ; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_2(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_2(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_2(ptr [[P]])
+; CHECK-NEXT:    call void @weak_odr_caller_of_foo_3(ptr [[P]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_2(
+; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_3(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @foo(ptr [[P]])
@@ -56,14 +56,14 @@ declare void @foo(ptr)
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_1(
+; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_2(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_2(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_3(
+; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_1(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_2(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
diff --git a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll
index 13d9fe3f98f28..c84cc4c1d8589 100644
--- a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll
+++ b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll
@@ -88,17 +88,17 @@ declare void @zar(ptr)
 ; CHECK-LABEL: define void @caller_of_callers(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_bar_2(ptr [[P]])
+; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_2(ptr [[P]])
+; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_3(ptr [[P]])
 ; CHECK-NEXT:    call void @linkonce_odr_caller_of_bar_2(ptr [[P]])
 ; CHECK-NEXT:    call void @linkonce_odr_caller_of_bar_2(ptr [[P]])
-; CHECK-NEXT:    call void @hidden_caller_of_zar_1(ptr [[P]])
-; CHECK-NEXT:    call void @hidden_caller_of_zar_1(ptr [[P]])
+; CHECK-NEXT:    call void @linkonce_odr_caller_of_bar_3(ptr [[P]])
+; CHECK-NEXT:    call void @linkonce_odr_caller_of_zar_2(ptr [[P]])
+; CHECK-NEXT:    call void @linkonce_odr_caller_of_zar_2(ptr [[P]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_foo_1(
+; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_foo_3(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @foo(ptr [[P]])
@@ -107,7 +107,7 @@ declare void @zar(ptr)
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_bar_2(
+; CHECK-LABEL: define private void @0(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @bar(ptr [[P]])
@@ -116,7 +116,7 @@ declare void @zar(ptr)
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define hidden void @hidden_caller_of_zar_1(
+; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_zar_2(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @zar(ptr [[P]])
@@ -124,3 +124,33 @@ declare void @zar(ptr)
 ; CHECK-NEXT:    tail call void @zar(ptr [[P]])
 ; CHECK-NEXT:    ret void
 ;
+;
+; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_foo_2(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_foo_1(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_bar_3(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @[[GLOB0:[0-9]+]](ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_bar_2(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define hidden void @hidden_caller_of_zar_1(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_zar_2(ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
diff --git a/llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll b/llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll
index 6c2b22fd1da7d..0f520d0ae4e44 100644
--- a/llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll
+++ b/llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll
@@ -42,12 +42,12 @@ declare void @foo(ptr)
 ; CHECK-LABEL: define void @caller_of_callers(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:    call void @weak_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_1(ptr [[P]])
+; CHECK-NEXT:    call void @weak_odr_caller_of_foo_2(ptr [[P]])
+; CHECK-NEXT:    call void @weak_odr_caller_of_foo_3(ptr [[P]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_1(
+; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_3(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @foo(ptr [[P]])
@@ -58,12 +58,12 @@ declare void @foo(ptr)
 ;
 ; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_2(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_1(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_3(
+; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_1(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_1(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
diff --git a/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll b/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll
index ef011cbb1bde0..d3eb85db65fe4 100644
--- a/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll
+++ b/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll
@@ -85,5 +85,72 @@ entry:
 }
 
 declare void @zar(ptr)
+; CHECK-LABEL: define void @caller_of_callers(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:    call void @weak_odr_caller_of_foo_1(ptr [[P]])
+; CHECK-NEXT:    call void @weak_odr_caller_of_foo_2(ptr [[P]])
+; CHECK-NEXT:    call void @weak_odr_caller_of_foo_3(ptr [[P]])
+; CHECK-NEXT:    call void @weak_odr_caller_of_bar_2(ptr [[P]])
+; CHECK-NEXT:    call void @weak_odr_caller_of_bar_2(ptr [[P]])
+; CHECK-NEXT:    call void @weak_odr_caller_of_bar_3(ptr [[P]])
+; CHECK-NEXT:    call void @weak_odr_caller_of_zar_2(ptr [[P]])
+; CHECK-NEXT:    call void @weak_odr_caller_of_zar_2(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_foo_3(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    tail call void @foo(ptr [[P]])
+; CHECK-NEXT:    tail call void @foo(ptr [[P]])
+; CHECK-NEXT:    tail call void @foo(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define private void @0(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    tail call void @bar(ptr [[P]])
+; CHECK-NEXT:    tail call void @bar(ptr [[P]])
+; CHECK-NEXT:    tail call void @bar(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_zar_2(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    tail call void @zar(ptr [[P]])
+; CHECK-NEXT:    tail call void @zar(ptr [[P]])
+; CHECK-NEXT:    tail call void @zar(ptr [[P]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_foo_2(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_foo_1(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_bar_3(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @[[GLOB0:[0-9]+]](ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_bar_2(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define hidden void @hidden_caller_of_zar_1(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @weak_odr_caller_of_zar_2(ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;

>From 02bd49478f6bcc138ceea5227e1f6947e5f01258 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sun, 23 Feb 2025 08:57:29 +0000
Subject: [PATCH 5/7] !fixup

---
 llvm/lib/Transforms/IPO/MergeFunctions.cpp    | 31 ++++++++-----
 .../test/Transforms/MergeFunc/linkonce_odr.ll |  8 ++--
 .../MergeFunc/merge-linkonce-odr-used.ll      | 18 +++++---
 .../merge-linkonce-odr-weak-odr-mixed-used.ll | 20 ++++++---
 .../MergeFunc/merge-linkonce-odr.ll           | 43 ++++++-------------
 .../MergeFunc/merge-weak-odr-used.ll          | 18 +++++---
 .../Transforms/MergeFunc/merge-weak-odr.ll    | 42 ++++++++++--------
 7 files changed, 97 insertions(+), 83 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index 11720d65a72a2..75ef5014ed811 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -889,16 +889,19 @@ bool MergeFunctions::writeThunkOrAlias(Function *F, Function *G) {
   return false;
 }
 
+/// Returns true if \p F is either weak_odr or linkonce_odr.
+static bool isODR(const Function *F) {
+  return F->hasWeakODRLinkage() || F->hasLinkOnceODRLinkage();
+}
+
 // Merge two equivalent functions. Upon completion, Function G is deleted.
 void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
 
   // Create a new thunk that both F and G can call, if F cannot call G directly.
   // That is the case if F is either interposable or if G is either weak_odr or
   // linkonce_odr.
-  if (F->isInterposable() || G->hasWeakODRLinkage() ||
-      G->hasLinkOnceODRLinkage()) {
-    assert(G->isInterposable() || G->hasWeakODRLinkage() ||
-           G->hasLinkOnceODRLinkage());
+  if (F->isInterposable() || (isODR(F) && isODR(G))) {
+    assert((!isODR(G) || isODR(F)) && "if G is ODR, F must also be ODR due to ordering");
 
     // Both writeThunkOrAlias() calls below must succeed, either because we can
     // create aliases for G and NewF, or because a thunk for F is profitable.
@@ -919,6 +922,13 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
     removeUsers(F);
     F->replaceAllUsesWith(NewF);
 
+    // If G or NewF are (weak|linkonce)_odr, update all callers to call the
+    // thunk.
+    if (isODR(G))
+      replaceDirectCallers(G, F);
+    if (isODR(F))
+      replaceDirectCallers(NewF, F);
+
     // We collect alignment before writeThunkOrAlias that overwrites NewF and
     // G's content.
     const MaybeAlign NewFAlign = NewF->getAlign();
@@ -992,19 +1002,18 @@ void MergeFunctions::replaceFunctionInTree(const FunctionNode &FN,
 
 // Ordering for functions that are equal under FunctionComparator
 static bool isFuncOrderCorrect(const Function *F, const Function *G) {
+  if (isODR(F) != isODR(G)) {
+    // ODR functions before non-ODR functions. A ODR function can call a non-ODR
+    // function if it is not interposable, but not the other way around.
+    return isODR(G);
+  }
+
   if (F->isInterposable() != G->isInterposable()) {
     // Strong before weak, because the weak function may call the strong
     // one, but not the other way around.
     return !F->isInterposable();
   }
 
-  if (F->hasWeakODRLinkage() != G->hasWeakODRLinkage() ||
-      F->hasLinkOnceODRLinkage() != G->hasLinkOnceODRLinkage()) {
-    // ODR functions before non-ODR functions. A ODR function can call a non-ODR
-    // function if it is not interposable, but not the other way around.
-    return F->hasWeakODRLinkage() || F->hasLinkOnceODRLinkage();
-  }
-
   if (F->hasLocalLinkage() != G->hasLocalLinkage()) {
     // External before local, because we definitely have to keep the external
     // function, but may be able to drop the local one.
diff --git a/llvm/test/Transforms/MergeFunc/linkonce_odr.ll b/llvm/test/Transforms/MergeFunc/linkonce_odr.ll
index 1a0e578f96790..ecbe6f08ab8c2 100644
--- a/llvm/test/Transforms/MergeFunc/linkonce_odr.ll
+++ b/llvm/test/Transforms/MergeFunc/linkonce_odr.ll
@@ -7,8 +7,6 @@
 ; The problem with this is that the linker could then choose these two stubs
 ; each of the two modules and we end up with two stubs calling each other.
 
-
-
 define linkonce_odr i32 @funC(i32 %x, i32 %y) {
   %sum = add i32 %x, %y
   %sum2 = add i32 %x, %sum
@@ -37,7 +35,7 @@ define linkonce_odr i32 @funA(i32 %x, i32 %y) {
 ;.
 ; CHECK: @take_addr_of_funB = global ptr @funB
 ;.
-; CHECK-LABEL: define linkonce_odr i32 @funA(
+; CHECK-LABEL: define private i32 @0(
 ; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
 ; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[X]], [[Y]]
 ; CHECK-NEXT:    [[SUM2:%.*]] = add i32 [[X]], [[SUM]]
@@ -47,12 +45,12 @@ define linkonce_odr i32 @funA(i32 %x, i32 %y) {
 ;
 ; CHECK-LABEL: define linkonce_odr i32 @funC(
 ; CHECK-SAME: i32 [[TMP0:%.*]], i32 [[TMP1:%.*]]) {
-; CHECK-NEXT:    [[TMP3:%.*]] = tail call i32 @funA(i32 [[TMP0]], i32 [[TMP1]])
+; CHECK-NEXT:    [[TMP3:%.*]] = tail call i32 @[[GLOB0:[0-9]+]](i32 [[TMP0]], i32 [[TMP1]])
 ; CHECK-NEXT:    ret i32 [[TMP3]]
 ;
 ;
 ; CHECK-LABEL: define linkonce_odr i32 @funB(
 ; CHECK-SAME: i32 [[TMP0:%.*]], i32 [[TMP1:%.*]]) {
-; CHECK-NEXT:    [[TMP3:%.*]] = tail call i32 @funA(i32 [[TMP0]], i32 [[TMP1]])
+; CHECK-NEXT:    [[TMP3:%.*]] = tail call i32 @[[GLOB0]](i32 [[TMP0]], i32 [[TMP1]])
 ; CHECK-NEXT:    ret i32 [[TMP3]]
 ;
diff --git a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-used.ll b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-used.ll
index 31b6aaffe9f63..db53a783058d1 100644
--- a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-used.ll
+++ b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-used.ll
@@ -41,13 +41,13 @@ declare void @foo(ptr)
 ;.
 ; CHECK-LABEL: define void @caller_of_callers(
 ; CHECK-SAME: ptr [[P:%.*]]) {
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_2(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_3(ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0:[0-9]+]](ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0]](ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0]](ptr [[P]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_3(
+; CHECK-LABEL: define private void @0(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @foo(ptr [[P]])
@@ -58,12 +58,18 @@ declare void @foo(ptr)
 ;
 ; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_2(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
 ;
 ; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_1(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_3(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
diff --git a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-weak-odr-mixed-used.ll b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-weak-odr-mixed-used.ll
index ee67dfb9be437..19f98d93f1ab1 100644
--- a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-weak-odr-mixed-used.ll
+++ b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr-weak-odr-mixed-used.ll
@@ -41,13 +41,13 @@ declare void @foo(ptr)
 ;.
 ; CHECK-LABEL: define void @caller_of_callers(
 ; CHECK-SAME: ptr [[P:%.*]]) {
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_2(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_3(ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0:[0-9]+]](ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0]](ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0]](ptr [[P]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_3(
+; CHECK-LABEL: define private void @0(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @foo(ptr [[P]])
@@ -56,14 +56,20 @@ declare void @foo(ptr)
 ; CHECK-NEXT:    ret void
 ;
 ;
+; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_1(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
 ; CHECK-LABEL: define linkonce_odr void @linkonce_odr_caller_of_foo_2(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_1(
+; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_3(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
diff --git a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll
index c84cc4c1d8589..7e815e1f50d64 100644
--- a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll
+++ b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll
@@ -85,20 +85,21 @@ entry:
 }
 
 declare void @zar(ptr)
+
 ; CHECK-LABEL: define void @caller_of_callers(
 ; CHECK-SAME: ptr [[P:%.*]]) {
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_2(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_foo_3(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_bar_2(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_bar_2(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_bar_3(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_zar_2(ptr [[P]])
-; CHECK-NEXT:    call void @linkonce_odr_caller_of_zar_2(ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0:[0-9]+]](ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0]](ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0]](ptr [[P]])
+; CHECK-NEXT:    call void @internal_caller_of_bar_1(ptr [[P]])
+; CHECK-NEXT:    call void @internal_caller_of_bar_1(ptr [[P]])
+; CHECK-NEXT:    call void @internal_caller_of_bar_1(ptr [[P]])
+; CHECK-NEXT:    call void @hidden_caller_of_zar_1(ptr [[P]])
+; CHECK-NEXT:    call void @hidden_caller_of_zar_1(ptr [[P]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_foo_3(
+; CHECK-LABEL: define private void @0(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @foo(ptr [[P]])
@@ -107,7 +108,7 @@ declare void @zar(ptr)
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define private void @0(
+; CHECK-LABEL: define internal void @internal_caller_of_bar_1(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @bar(ptr [[P]])
@@ -116,7 +117,7 @@ declare void @zar(ptr)
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_zar_2(
+; CHECK-LABEL: define hidden void @hidden_caller_of_zar_1(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @zar(ptr [[P]])
@@ -127,30 +128,12 @@ declare void @zar(ptr)
 ;
 ; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_foo_2(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
 ;
 ; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_foo_1(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_foo_3(ptr [[TMP0]])
-; CHECK-NEXT:    ret void
-;
-;
-; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_bar_3(
-; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @[[GLOB0:[0-9]+]](ptr [[TMP0]])
-; CHECK-NEXT:    ret void
-;
-;
-; CHECK-LABEL: define linkonce_odr hidden void @linkonce_odr_caller_of_bar_2(
-; CHECK-SAME: ptr [[TMP0:%.*]]) {
 ; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
-;
-; CHECK-LABEL: define hidden void @hidden_caller_of_zar_1(
-; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @linkonce_odr_caller_of_zar_2(ptr [[TMP0]])
-; CHECK-NEXT:    ret void
-;
diff --git a/llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll b/llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll
index 0f520d0ae4e44..601a1275ec2f8 100644
--- a/llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll
+++ b/llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll
@@ -41,13 +41,13 @@ declare void @foo(ptr)
 ;.
 ; CHECK-LABEL: define void @caller_of_callers(
 ; CHECK-SAME: ptr [[P:%.*]]) {
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_2(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_3(ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0:[0-9]+]](ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0]](ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0]](ptr [[P]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_3(
+; CHECK-LABEL: define private void @0(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @foo(ptr [[P]])
@@ -58,12 +58,18 @@ declare void @foo(ptr)
 ;
 ; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_2(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
 ;
 ; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_1(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define weak_odr void @weak_odr_caller_of_foo_3(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
diff --git a/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll b/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll
index d3eb85db65fe4..01acad06c50ce 100644
--- a/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll
+++ b/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll
@@ -87,18 +87,18 @@ entry:
 declare void @zar(ptr)
 ; CHECK-LABEL: define void @caller_of_callers(
 ; CHECK-SAME: ptr [[P:%.*]]) {
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_1(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_2(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_foo_3(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_bar_2(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_bar_2(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_bar_3(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_zar_2(ptr [[P]])
-; CHECK-NEXT:    call void @weak_odr_caller_of_zar_2(ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0:[0-9]+]](ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0]](ptr [[P]])
+; CHECK-NEXT:    call void @[[GLOB0]](ptr [[P]])
+; CHECK-NEXT:    call void @internal_caller_of_bar_1(ptr [[P]])
+; CHECK-NEXT:    call void @internal_caller_of_bar_1(ptr [[P]])
+; CHECK-NEXT:    call void @internal_caller_of_bar_1(ptr [[P]])
+; CHECK-NEXT:    call void @hidden_caller_of_zar_1(ptr [[P]])
+; CHECK-NEXT:    call void @hidden_caller_of_zar_1(ptr [[P]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_foo_3(
+; CHECK-LABEL: define private void @0(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @foo(ptr [[P]])
@@ -107,7 +107,7 @@ declare void @zar(ptr)
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define private void @0(
+; CHECK-LABEL: define internal void @internal_caller_of_bar_1(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @bar(ptr [[P]])
@@ -116,7 +116,7 @@ declare void @zar(ptr)
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_zar_2(
+; CHECK-LABEL: define hidden void @hidden_caller_of_zar_1(
 ; CHECK-SAME: ptr [[P:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    tail call void @zar(ptr [[P]])
@@ -127,30 +127,36 @@ declare void @zar(ptr)
 ;
 ; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_foo_2(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
 ;
 ; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_foo_1(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_foo_3(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_bar_3(
+; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_foo_3(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @[[GLOB0:[0-9]+]](ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
 ;
 ; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_bar_2(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @[[GLOB0]](ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @internal_caller_of_bar_1(ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;
 ;
-; CHECK-LABEL: define hidden void @hidden_caller_of_zar_1(
+; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_bar_3(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @internal_caller_of_bar_1(ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_zar_2(
 ; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT:    tail call void @weak_odr_caller_of_zar_2(ptr [[TMP0]])
+; CHECK-NEXT:    tail call void @hidden_caller_of_zar_1(ptr [[TMP0]])
 ; CHECK-NEXT:    ret void
 ;

>From c077b08bc02eeb8b047d6c403ce856ca95e73385 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 25 Feb 2025 12:03:53 +0000
Subject: [PATCH 6/7] !fixup fix formatting

---
 llvm/lib/Transforms/IPO/MergeFunctions.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index 75ef5014ed811..9cc159830b17d 100644
--- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp
+++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
@@ -901,7 +901,8 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
   // That is the case if F is either interposable or if G is either weak_odr or
   // linkonce_odr.
   if (F->isInterposable() || (isODR(F) && isODR(G))) {
-    assert((!isODR(G) || isODR(F)) && "if G is ODR, F must also be ODR due to ordering");
+    assert((!isODR(G) || isODR(F)) &&
+           "if G is ODR, F must also be ODR due to ordering");
 
     // Both writeThunkOrAlias() calls below must succeed, either because we can
     // create aliases for G and NewF, or because a thunk for F is profitable.

>From 354817f11d4b9702200ee23a0252768c445996ad Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 25 Feb 2025 14:04:44 +0000
Subject: [PATCH 7/7] !fixup update clang test

---
 clang/test/CodeGenCXX/merge-functions.cpp | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/clang/test/CodeGenCXX/merge-functions.cpp b/clang/test/CodeGenCXX/merge-functions.cpp
index 892234ceedd37..d3afd8086f41a 100644
--- a/clang/test/CodeGenCXX/merge-functions.cpp
+++ b/clang/test/CodeGenCXX/merge-functions.cpp
@@ -1,6 +1,6 @@
 // REQUIRES: x86-registered-target
-// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -O0 -fmerge-functions -emit-llvm -o - -x c++ < %s | FileCheck %s -implicit-check-not=_ZN1A1gEiPi
-// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -O1 -fmerge-functions -emit-llvm -o - -x c++ < %s | FileCheck %s -implicit-check-not=_ZN1A1gEiPi
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -O0 -fmerge-functions -emit-llvm -o - -x c++ < %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -O1 -fmerge-functions -emit-llvm -o - -x c++ < %s | FileCheck %s
 
 // Basic functionality test. Function merging doesn't kick in on functions that
 // are too simple.
@@ -10,4 +10,8 @@ struct A {
   virtual int g(int x, int *p) { return x ? *p : 1; }
 } a;
 
-// CHECK: define {{.*}} @_ZN1A1fEiPi
+// CHECK: define linkonce_odr noundef i32 @_ZN1A1gEiPi(
+// CHECK:   tail call noundef i32 @0(
+
+// CHECK: define linkonce_odr noundef i32 @_ZN1A1fEiPi(
+// CHECK:   tail call noundef i32 @0(



More information about the cfe-commits mailing list