Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions clang/test/CodeGenCXX/merge-functions.cpp
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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(
29 changes: 27 additions & 2 deletions llvm/lib/Transforms/IPO/MergeFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,10 +889,20 @@ 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) {
if (F->isInterposable()) {
assert(G->isInterposable());

// 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() || (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.
Expand All @@ -913,6 +923,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();
Expand Down Expand Up @@ -986,16 +1003,24 @@ 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->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.
Expand Down
5 changes: 3 additions & 2 deletions llvm/test/Transforms/MergeFunc/comdat.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

12 changes: 8 additions & 4 deletions llvm/test/Transforms/MergeFunc/linkonce_odr.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -37,16 +35,22 @@ 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]]
; CHECK-NEXT: [[SUM3:%.*]] = add i32 [[X]], [[SUM2]]
; CHECK-NEXT: ret i32 [[SUM3]]
;
;
; CHECK-LABEL: define linkonce_odr i32 @funC(
; CHECK-SAME: 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]]
;
18 changes: 12 additions & 6 deletions llvm/test/Transforms/MergeFunc/merge-linkonce-odr-used.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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_1(ptr [[P]])
; CHECK-NEXT: call void @linkonce_odr_caller_of_foo_1(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_1(
; CHECK-LABEL: define private void @0(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @foo(ptr [[P]])
Expand All @@ -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_1(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 @[[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 @linkonce_odr_caller_of_foo_1(ptr [[TMP0]])
; CHECK-NEXT: tail call void @[[GLOB0]](ptr [[TMP0]])
; CHECK-NEXT: ret void
;
Original file line number Diff line number Diff line change
Expand Up @@ -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_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 @[[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_2(
; CHECK-LABEL: define private void @0(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @foo(ptr [[P]])
Expand All @@ -58,12 +58,18 @@ declare void @foo(ptr)
;
; 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 @[[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 @[[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 @linkonce_odr_caller_of_foo_2(ptr [[TMP0]])
; CHECK-NEXT: tail call void @[[GLOB0]](ptr [[TMP0]])
; CHECK-NEXT: ret void
;
29 changes: 21 additions & 8 deletions llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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_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_bar_2(ptr [[P]])
; CHECK-NEXT: call void @linkonce_odr_caller_of_bar_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_1(
; CHECK-LABEL: define private void @0(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @foo(ptr [[P]])
Expand All @@ -107,7 +108,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 internal void @internal_caller_of_bar_1(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @bar(ptr [[P]])
Expand All @@ -124,3 +125,15 @@ 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 @[[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 @[[GLOB0]](ptr [[TMP0]])
; CHECK-NEXT: ret void
;
18 changes: 12 additions & 6 deletions llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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_1(ptr [[P]])
; CHECK-NEXT: call void @weak_odr_caller_of_foo_1(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_1(
; CHECK-LABEL: define private void @0(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @foo(ptr [[P]])
Expand All @@ -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_1(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 @[[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 @weak_odr_caller_of_foo_1(ptr [[TMP0]])
; CHECK-NEXT: tail call void @[[GLOB0]](ptr [[TMP0]])
; CHECK-NEXT: ret void
;
34 changes: 23 additions & 11 deletions llvm/test/Transforms/MergeFunc/merge-weak-odr.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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_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 @[[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_1(
; CHECK-LABEL: define private void @0(
; CHECK-SAME: ptr [[P:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: tail call void @foo(ptr [[P]])
Expand All @@ -107,7 +107,7 @@ declare void @zar(ptr)
; CHECK-NEXT: ret void
;
;
; CHECK-LABEL: define weak_odr hidden void @weak_odr_caller_of_bar_2(
; 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]])
Expand All @@ -127,19 +127,31 @@ 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_1(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 @[[GLOB0]](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: 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 @internal_caller_of_bar_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: tail call void @internal_caller_of_bar_1(ptr [[TMP0]])
; CHECK-NEXT: ret void
;
;
Expand Down