Author: Florian Hahn Date: 2025-02-25T15:55:25Z New Revision: f10e0f7321b34693697a0bf895d440f82b32ba54
URL: https://github.com/llvm/llvm-project/commit/f10e0f7321b34693697a0bf895d440f82b32ba54 DIFF: https://github.com/llvm/llvm-project/commit/f10e0f7321b34693697a0bf895d440f82b32ba54.diff LOG: [MergeFuncs] Don't introduce calls to (linkonce,weak)_odr functions. (#125050) Avoid creating new calls to linkonce_odr/weak_odr functions when merging 2 functions, as this may introduce an infinite call cycle. Consider 2 functions below, both present in 2 modules. Module X -- define linkonce_odr void @"A"() { call void @"foo"() } define linkonce_odr void @"B"() { call void @"foo"() } --- Module Y --- global @"g" = @"B" define linkonce_odr void @"A"() { %l = load @"g" call void %l() } define linkonce_odr void @"B"() { call void @"foo"() } --- @"A" and @"B" in both modules are semantically equivalent Module X after function merging: --- define linkonce_odr void @"A"() { call void @"foo"() } define linkonce_odr void @"B"() { call void @"A"() } --- Module Y is unchanged. Then the linker picks @"A" from module Y and @"B" from module X. Now there's an infinite call cycle PR: https://github.com/llvm/llvm-project/pull/125050 Added: Modified: clang/test/CodeGenCXX/merge-functions.cpp llvm/lib/Transforms/IPO/MergeFunctions.cpp llvm/test/Transforms/MergeFunc/comdat.ll llvm/test/Transforms/MergeFunc/linkonce_odr.ll llvm/test/Transforms/MergeFunc/merge-linkonce-odr-used.ll llvm/test/Transforms/MergeFunc/merge-linkonce-odr-weak-odr-mixed-used.ll llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll llvm/test/Transforms/MergeFunc/merge-weak-odr.ll Removed: ################################################################################ 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( diff --git a/llvm/lib/Transforms/IPO/MergeFunctions.cpp b/llvm/lib/Transforms/IPO/MergeFunctions.cpp index e8508416f5427..9cc159830b17d 100644 --- a/llvm/lib/Transforms/IPO/MergeFunctions.cpp +++ b/llvm/lib/Transforms/IPO/MergeFunctions.cpp @@ -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. @@ -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(); @@ -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. 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) { diff --git a/llvm/test/Transforms/MergeFunc/linkonce_odr.ll b/llvm/test/Transforms/MergeFunc/linkonce_odr.ll index 14b56a8ece30e..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]] @@ -45,8 +43,14 @@ 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 @[[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 fe6a5210454b6..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_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]]) @@ -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 ; 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..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 @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]]) @@ -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 ; diff --git a/llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll b/llvm/test/Transforms/MergeFunc/merge-linkonce-odr.ll index 13d9fe3f98f28..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_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]]) @@ -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]]) @@ -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 +; diff --git a/llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll b/llvm/test/Transforms/MergeFunc/merge-weak-odr-used.ll index 6c2b22fd1da7d..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_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]]) @@ -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 ; diff --git a/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll b/llvm/test/Transforms/MergeFunc/merge-weak-odr.ll index 3ea279a411c72..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_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]]) @@ -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]]) @@ -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 ; ; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits