[llvm] [MergeFuncs] Don't introduce calls to weak_odr functions. (PR #125050)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 30 03:46:16 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Florian Hahn (fhahn)

<details>
<summary>Changes</summary>

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
    }

---
Full diff: https://github.com/llvm/llvm-project/pull/125050.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/IPO/MergeFunctions.cpp (+2-2) 
- (added) llvm/test/Transforms/MergeFunc/merge-weak-odr.ll (+112) 


``````````diff
diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp
index e8508416f54275..d991ec64445ee9 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
new file mode 100644
index 00000000000000..d2e89092b9a826
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll
@@ -0,0 +1,112 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --include-generated-funcs --version 5
+; RUN: opt -p mergefunc -S %s | FileCheck %s
+
+define weak_odr hidden void @weak_odr_caller_of_foo_1(ptr %p) {
+entry:
+  tail call void @foo(ptr %p)
+  tail call void @foo(ptr %p)
+  tail call void @foo(ptr %p)
+  ret void
+}
+
+define weak_odr hidden void @weak_odr_caller_of_foo_2(ptr %p) {
+entry:
+  tail call void @foo(ptr %p)
+  tail call void @foo(ptr %p)
+  tail call void @foo(ptr %p)
+  ret void
+}
+
+declare void @foo(ptr)
+
+define hidden void @weak_odr_caller_of_bar_1(ptr %p) {
+entry:
+  tail call void @bar(ptr %p)
+  tail call void @bar(ptr %p)
+  tail call void @bar(ptr %p)
+  ret void
+}
+
+define weak_odr hidden void @non_weak_caller_of_bar_2(ptr %p) {
+entry:
+  tail call void @bar(ptr %p)
+  tail call void @bar(ptr %p)
+  tail call void @bar(ptr %p)
+  ret void
+}
+
+declare void @bar(ptr)
+
+define hidden void @non_weak_caller_of_zar_1(ptr %p) {
+entry:
+  tail call void @zar(ptr %p)
+  tail call void @zar(ptr %p)
+  tail call void @zar(ptr %p)
+  ret void
+}
+
+define weak_odr hidden void @weak_odr_caller_of_zar_2(ptr %p) {
+entry:
+  tail call void @zar(ptr %p)
+  tail call void @zar(ptr %p)
+  tail call void @zar(ptr %p)
+  ret void
+}
+
+declare void @zar(ptr)
+; CHECK-LABEL: define private void @0(
+; 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 @non_weak_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 private void @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 @[[GLOB0:[0-9]+]](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 @[[GLOB0]](ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define hidden void @weak_odr_caller_of_bar_1(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @non_weak_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 @[[GLOB1:[0-9]+]](ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;
+;
+; CHECK-LABEL: define hidden void @non_weak_caller_of_zar_1(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT:    tail call void @[[GLOB1]](ptr [[TMP0]])
+; CHECK-NEXT:    ret void
+;

``````````

</details>


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


More information about the llvm-commits mailing list