diff --git a/clang/test/CodeGenCXX/pr51066.cpp b/clang/test/CodeGenCXX/pr51066.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/pr51066.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-fuchsia -emit-obj -massembler-fatal-warnings --mrelax-relocations -disable-free -disable-llvm-verifier -discard-value-names -main-file-name zircon_platform_buffer.cc -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fno-rounding-math -mconstructor-aliases -target-cpu x86-64-v2 -mllvm -x86-branches-within-32B-boundaries -debug-info-kind=constructor -dwarf-version=5 -debugger-tuning=gdb -mllvm -crash-diagnostics-dir=clang-crashreports -ffunction-sections -fdata-sections -fcoverage-compilation-dir=. -sys-header-deps -D TOOLCHAIN_VERSION=dO8igrHyLDfgq8txd8Qx0mI9oJqFLswAMLcIBKLF6TYC -D _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D NDEBUG=1 -D _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS=1 -D MAGMA_DEBUG_INTERNAL_USE_ONLY=0 -D MAGMA_ENABLE_TRACING -O3 -Wall -Wextra -Wnewline-eof -Wconversion -Wimplicit-fallthrough -Wno-unused-parameter -Wno-sign-conversion -Wno-c99-designator -Werror -Wno-error=deprecated-declarations -Wthread-safety -Wno-deprecated-copy -Wno-suggest-override -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=. -ferror-limit 19 -fvisibility=hidden -fvisibility-inlines-hidden -fsanitize=safe-stack -stack-protector 2 -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-legacy-pass-manager -fcolor-diagnostics -vectorize-loops -vectorize-slp -debug-info-kind=constructor -fmerge-functions -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -x c++ %s -o /dev/null +// PR51066 - Just ensure this doesn't crash. + +class a { + public: + virtual int f(); + virtual int g(); + static a* x(); + static a* y(); +}; +class b : public a { + public: + int f() override; +}; +class c { + public: + static int z(); +}; +int c::z() { return a::x()->g(); } +a* a::x() { return y(); } +a* a::y() { return new b(); } +int b::f() { return 0; } +int s() { return a::x()->f(); } 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,18 @@ +;; Check the cases involving dso_local_equivalent where we do expect functions to be merged. +; RUN: opt -S -mergefunc < %s | FileCheck %s + +declare i32 @func() + +; CHECK-LABEL: @call1 +; CHECK: call i32 dso_local_equivalent @func() +define i32 @call1() { + %1 = call i32 dso_local_equivalent @func() + ret i32 %1 +} + +; CHECK-LABEL: @call2 +; CHECK: call i32 dso_local_equivalent @func() +define i32 @call2() { + %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,45 @@ +;; Check the cases involving dso_local_equivalent where we do not expect functions to be merged. +; RUN: opt -S -mergefunc < %s | FileCheck %s + +;; func1 and func2 are different functions. +declare i32 @func1() +define i32 @func2() { + ret i32 0 +} + +; CHECK-LABEL: @call1 +; CHECK: call i32 dso_local_equivalent @func1() +define i32 @call1() { + %1 = call i32 dso_local_equivalent @func1() + ret i32 %1 +} + +; CHECK-LABEL: @call2 +; CHECK: call i32 dso_local_equivalent @func2() +define i32 @call2() { + %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() { + ret i32 0 +} +define i32 @func4() { + ret i32 0 +} + +; CHECK-LABEL: @call3 +; CHECK: call i32 dso_local_equivalent @func3() +define i32 @call3() { + %1 = call i32 dso_local_equivalent @func3() + ret i32 %1 +} + +; CHECK-LABEL: @call4 +; CHECK: call i32 dso_local_equivalent @func4() +define i32 @call4() { + %1 = call i32 dso_local_equivalent @func4() + ret i32 %1 +}