diff --git a/llvm/lib/Transforms/Utils/FunctionComparator.cpp b/llvm/lib/Transforms/Utils/FunctionComparator.cpp --- a/llvm/lib/Transforms/Utils/FunctionComparator.cpp +++ b/llvm/lib/Transforms/Utils/FunctionComparator.cpp @@ -402,6 +402,15 @@ return cmpValues(LBA->getBasicBlock(), RBA->getBasicBlock()); } } + case Value::DSOLocalEquivalentVal: { + // dso_local_equivalent is functionally equivalent to whatever it points to. + // This means the behavior of the IR should be the exact same as if the + // function was referenced directly rather than through a + // dso_local_equivalent. + const auto *LEquiv = cast(L); + const auto *REquiv = cast(R); + return cmpGlobalValues(LEquiv->getGlobalValue(), REquiv->getGlobalValue()); + } default: // Unknown constant, abort. LLVM_DEBUG(dbgs() << "Looking at valueID " << L->getValueID() << "\n"); llvm_unreachable("Constant ValueID not recognized."); diff --git a/llvm/test/Transforms/MergeFunc/dso_local_equivalent_merged.ll b/llvm/test/Transforms/MergeFunc/dso_local_equivalent_merged.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/MergeFunc/dso_local_equivalent_merged.ll @@ -0,0 +1,29 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +;; Check the cases involving dso_local_equivalent where we do expect functions to be merged. +; RUN: opt -S -mergefunc < %s | FileCheck %s + +; CHECK-NOT: @b + +@x = constant { i32 ()*, i32 ()* } { i32 ()* @a, i32 ()* @b } +; CHECK: { i32 ()* @a, i32 ()* @a } + +define i32 @func() { +; CHECK-LABEL: @func( +; CHECK-NEXT: ret i32 0 +; + ret i32 0 +} + +define internal i32 @a() unnamed_addr { +; CHECK-LABEL: @a( +; CHECK-NEXT: [[TMP1:%.*]] = call i32 dso_local_equivalent @func() +; CHECK-NEXT: ret i32 [[TMP1]] +; + %1 = call i32 dso_local_equivalent @func() + ret i32 %1 +} + +define internal i32 @b() unnamed_addr { + %1 = call i32 dso_local_equivalent @func() + ret i32 %1 +} diff --git a/llvm/test/Transforms/MergeFunc/dso_local_equivalent_unmerged.ll b/llvm/test/Transforms/MergeFunc/dso_local_equivalent_unmerged.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/MergeFunc/dso_local_equivalent_unmerged.ll @@ -0,0 +1,69 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +;; Check the cases involving dso_local_equivalent where we do not expect functions to be merged. +; RUN: opt -S -mergefunc < %s | FileCheck %s + +@x = constant { i32 ()*, i32 ()* } { i32 ()* @a, i32 ()* @b } +; CHECK: { i32 ()* @a, i32 ()* @b } + +@x2 = constant { i32 ()*, i32 ()* } { i32 ()* @c, i32 ()* @d } +; CHECK: { i32 ()* @c, i32 ()* @d } + +;; func1 and func2 are different functions. +declare i32 @func1() +define i32 @func2() { +; CHECK-LABEL: @func2( +; CHECK-NEXT: ret i32 0 +; + ret i32 0 +} + +define internal i32 @a() unnamed_addr { +; CHECK-LABEL: @a( +; CHECK-NEXT: [[TMP1:%.*]] = call i32 dso_local_equivalent @func1() +; CHECK-NEXT: ret i32 [[TMP1]] +; + %1 = call i32 dso_local_equivalent @func1() + ret i32 %1 +} + +define internal i32 @b() unnamed_addr { +; CHECK-LABEL: @b( +; CHECK-NEXT: [[TMP1:%.*]] = call i32 dso_local_equivalent @func2() +; CHECK-NEXT: ret i32 [[TMP1]] +; + %1 = call i32 dso_local_equivalent @func2() + ret i32 %1 +} + +;; func3 and func4 have the same body and signature but do not have merged +;; callers because they are different functions. +define i32 @func3() { +; CHECK-LABEL: @func3( +; CHECK-NEXT: ret i32 0 +; + ret i32 0 +} +define i32 @func4() { +; CHECK-LABEL: @func4( +; CHECK-NEXT: ret i32 0 +; + ret i32 0 +} + +define internal i32 @c() unnamed_addr { +; CHECK-LABEL: @c( +; CHECK-NEXT: [[TMP1:%.*]] = call i32 dso_local_equivalent @func3() +; CHECK-NEXT: ret i32 [[TMP1]] +; + %1 = call i32 dso_local_equivalent @func3() + ret i32 %1 +} + +define internal i32 @d() unnamed_addr { +; CHECK-LABEL: @d( +; CHECK-NEXT: [[TMP1:%.*]] = call i32 dso_local_equivalent @func4() +; CHECK-NEXT: ret i32 [[TMP1]] +; + %1 = call i32 dso_local_equivalent @func4() + ret i32 %1 +}