[llvm] 2f51e5b - [MergeFunc] Preserve observable function pointer identity (#213604)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 24 02:21:17 PDT 2026
Author: Gauarv Chaudhary
Date: 2026-08-24T09:21:12Z
New Revision: 2f51e5b46ac4bcaea1ff7b504d484709f908ac0b
URL: https://github.com/llvm/llvm-project/commit/2f51e5b46ac4bcaea1ff7b504d484709f908ac0b
DIFF: https://github.com/llvm/llvm-project/commit/2f51e5b46ac4bcaea1ff7b504d484709f908ac0b.diff
LOG: [MergeFunc] Preserve observable function pointer identity (#213604)
Fixes #213206.
MergeFunc can replace one function with a forwarding thunk to another.
FunctionComparator treated self-references as equal in every context, so
functions that observe their own address (e.g. icmp %p, @g) could be
merged incorrectly.
Compare ordinary function-pointer uses as normal global values. Treat
self-references as equal only for corresponding call targets and
blockaddress.
AI assistance was used while preparing this change.
Added:
llvm/test/Transforms/MergeFunc/recursive-self-reference.ll
llvm/test/Transforms/MergeFunc/self-reference.ll
Modified:
llvm/include/llvm/Transforms/Utils/FunctionComparator.h
llvm/lib/Transforms/Utils/FunctionComparator.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
index d765875864a89..abe22bc2cade8 100644
--- a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
+++ b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
@@ -226,9 +226,6 @@ class FunctionComparator {
/// return whether the numbers are equal. Numbers are assigned in the order
/// visited.
/// Comparison order:
- /// Stage 0: Value that is function itself is always greater then others.
- /// If left and right values are references to their functions, then
- /// they are equal.
/// Stage 1: Constants are greater than non-constants.
/// If both left and right are constants, then the result of
/// cmpConstants is used as cmpValues result.
@@ -242,6 +239,11 @@ class FunctionComparator {
/// see comments for sn_mapL and sn_mapR.
LLVM_ABI int cmpValues(const Value *L, const Value *R) const;
+ /// References to the functions being compared are equal. Used for
+ /// corresponding call targets and blockaddress functions, which stay
+ /// valid if MergeFunc replaces one body with a forwarding thunk.
+ int cmpValuesAllowingSelfRef(const Value *L, const Value *R) const;
+
/// Compare two Instructions for equivalence, similar to
/// Instruction::isSameOperationAs.
///
diff --git a/llvm/lib/Transforms/Utils/FunctionComparator.cpp b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
index 05c24af6efb39..679d4dbfeff54 100644
--- a/llvm/lib/Transforms/Utils/FunctionComparator.cpp
+++ b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
@@ -478,7 +478,8 @@ int FunctionComparator::cmpConstants(const Constant *L,
case Value::BlockAddressVal: {
const BlockAddress *LBA = cast<BlockAddress>(L);
const BlockAddress *RBA = cast<BlockAddress>(R);
- if (int Res = cmpValues(LBA->getFunction(), RBA->getFunction()))
+ if (int Res =
+ cmpValuesAllowingSelfRef(LBA->getFunction(), RBA->getFunction()))
return Res;
if (LBA->getFunction() == RBA->getFunction()) {
// They are BBs in the same function. Order by which comes first in the
@@ -891,23 +892,23 @@ int FunctionComparator::cmpInlineAsm(const InlineAsm *L,
return 0;
}
-/// Compare two values used by the two functions under pair-wise comparison. If
-/// this is the first time the values are seen, they're added to the mapping so
-/// that we will detect mismatches on next use.
-/// See comments in declaration for more details.
-int FunctionComparator::cmpValues(const Value *L, const Value *R) const {
- // Catch self-reference case.
+int FunctionComparator::cmpValuesAllowingSelfRef(const Value *L,
+ const Value *R) const {
if (L == FnL) {
if (R == FnR)
return 0;
return -1;
}
- if (R == FnR) {
- if (L == FnL)
- return 0;
+ if (R == FnR)
return 1;
- }
+ return cmpValues(L, R);
+}
+/// Compare two values used by the two functions under pair-wise comparison. If
+/// this is the first time the values are seen, they're added to the mapping so
+/// that we will detect mismatches on next use.
+/// See comments in declaration for more details.
+int FunctionComparator::cmpValues(const Value *L, const Value *R) const {
const Constant *ConstL = dyn_cast<Constant>(L);
const Constant *ConstR = dyn_cast<Constant>(R);
if (ConstL && ConstR) {
@@ -964,11 +965,19 @@ int FunctionComparator::cmpBasicBlocks(const BasicBlock *BBL,
return Res;
if (needToCmpOperands) {
assert(InstL->getNumOperands() == InstR->getNumOperands());
+ const auto *CBL = dyn_cast<CallBase>(&*InstL);
for (unsigned i = 0, e = InstL->getNumOperands(); i != e; ++i) {
Value *OpL = InstL->getOperand(i);
Value *OpR = InstR->getOperand(i);
- if (int Res = cmpValues(OpL, OpR))
+ int Res;
+ if (CBL && CBL->isCallee(&InstL->getOperandUse(i))) {
+ assert(cast<CallBase>(&*InstR)->isCallee(&InstR->getOperandUse(i)));
+ Res = cmpValuesAllowingSelfRef(OpL, OpR);
+ } else {
+ Res = cmpValues(OpL, OpR);
+ }
+ if (Res)
return Res;
// cmpValues should ensure this is true.
assert(cmpTypes(OpL->getType(), OpR->getType()) == 0);
diff --git a/llvm/test/Transforms/MergeFunc/recursive-self-reference.ll b/llvm/test/Transforms/MergeFunc/recursive-self-reference.ll
new file mode 100644
index 0000000000000..7478696b43828
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/recursive-self-reference.ll
@@ -0,0 +1,29 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=mergefunc < %s | FileCheck %s
+
+; Recursive call targets may still be considered equivalent.
+
+define internal void @recursive_f() {
+; CHECK-LABEL: define internal void @recursive_f() {
+; CHECK-NEXT: call void @recursive_f()
+; CHECK-NEXT: ret void
+;
+ call void @recursive_f()
+ ret void
+}
+
+define internal void @recursive_g() {
+ call void @recursive_g()
+ ret void
+}
+
+define i32 @main() {
+; CHECK-LABEL: define i32 @main() {
+; CHECK-NEXT: call void @recursive_f()
+; CHECK-NEXT: call void @recursive_f()
+; CHECK-NEXT: ret i32 0
+;
+ call void @recursive_f()
+ call void @recursive_g()
+ ret i32 0
+}
diff --git a/llvm/test/Transforms/MergeFunc/self-reference.ll b/llvm/test/Transforms/MergeFunc/self-reference.ll
new file mode 100644
index 0000000000000..99f6a87e44c0b
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/self-reference.ll
@@ -0,0 +1,67 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=mergefunc < %s | FileCheck %s
+
+; A function's address is observable when it is used as a value, so it must not
+; be replaced with the address of a forwarding thunk's target.
+
+define void @f(ptr %p) {
+; CHECK-LABEL: define void @f(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[P]], @f
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: ret void
+;
+ %cmp = icmp eq ptr %p, @f
+ call void @llvm.assume(i1 %cmp)
+ ret void
+}
+
+define void @g(ptr %p) {
+; CHECK-LABEL: define void @g(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[P]], @g
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: ret void
+;
+ %cmp = icmp eq ptr %p, @g
+ call void @llvm.assume(i1 %cmp)
+ ret void
+}
+
+; A self-reference passed as an argument is also observable and must remain a
+; normal value comparison rather than a call-target comparison.
+
+declare void @consume(ptr)
+
+define void @arg_f() {
+; CHECK-LABEL: define void @arg_f() {
+; CHECK-NEXT: call void @consume(ptr @arg_f)
+; CHECK-NEXT: ret void
+;
+ call void @consume(ptr @arg_f)
+ ret void
+}
+
+define void @arg_g() {
+; CHECK-LABEL: define void @arg_g() {
+; CHECK-NEXT: call void @consume(ptr @arg_g)
+; CHECK-NEXT: ret void
+;
+ call void @consume(ptr @arg_g)
+ ret void
+}
+
+define i32 @main() {
+; CHECK-LABEL: define i32 @main() {
+; CHECK-NEXT: call void @f(ptr @f)
+; CHECK-NEXT: call void @g(ptr @g)
+; CHECK-NEXT: call void @arg_f()
+; CHECK-NEXT: call void @arg_g()
+; CHECK-NEXT: ret i32 0
+;
+ call void @f(ptr @f)
+ call void @g(ptr @g)
+ call void @arg_f()
+ call void @arg_g()
+ ret i32 0
+}
More information about the llvm-commits
mailing list