[llvm] [MergeFunc] Preserve observable function pointer identity (PR #213604)
Gauarv Chaudhary via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 22:50:45 PDT 2026
https://github.com/ANAMASGARD updated https://github.com/llvm/llvm-project/pull/213604
>From d67332f5ca316674e7ba0120cabe8b057c8dec20 Mon Sep 17 00:00:00 2001
From: Gaurav Chaudhary <chaudharygaurav2004 at gmail.com>
Date: Mon, 3 Aug 2026 12:17:02 +0530
Subject: [PATCH 1/2] [MergeFunc] Preserve observable function pointer identity
Signed-off-by: Gaurav Chaudhary <chaudharygaurav2004 at gmail.com>
---
.../Transforms/Utils/FunctionComparator.h | 12 +++--
.../Transforms/Utils/FunctionComparator.cpp | 37 +++++++++-----
.../MergeFunc/recursive-self-reference.ll | 25 ++++++++++
.../Transforms/MergeFunc/self-reference.ll | 50 +++++++++++++++++++
.../Utils/FunctionComparatorTest.cpp | 46 +++++++++++++++++
5 files changed, 153 insertions(+), 17 deletions(-)
create mode 100644 llvm/test/Transforms/MergeFunc/recursive-self-reference.ll
create mode 100644 llvm/test/Transforms/MergeFunc/self-reference.ll
diff --git a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
index d765875864a89..0acf1e51d8cd1 100644
--- a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
+++ b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
@@ -101,6 +101,8 @@ class FunctionComparator {
LLVM_ABI int compare();
protected:
+ enum class ValueComparisonKind { Normal, CallTarget, BlockAddress };
+
/// Start the comparison.
void beginCompare() {
sn_mapL.clear();
@@ -226,9 +228,9 @@ 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 0: In CallTarget or BlockAddress comparison mode, references to the
+ /// functions being compared are equal. In Normal comparison mode,
+ /// function values are compared like other global values.
/// 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.
@@ -240,7 +242,9 @@ class FunctionComparator {
/// then left value is greater.
/// In another words, we compare serial numbers, for more details
/// see comments for sn_mapL and sn_mapR.
- LLVM_ABI int cmpValues(const Value *L, const Value *R) const;
+ LLVM_ABI int cmpValues(
+ const Value *L, const Value *R,
+ ValueComparisonKind Kind = ValueComparisonKind::Normal) 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 c9cc62adc2604..c213bb06fed42 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 = cmpValues(LBA->getFunction(), RBA->getFunction(),
+ ValueComparisonKind::BlockAddress))
return Res;
if (LBA->getFunction() == RBA->getFunction()) {
// They are BBs in the same function. Order by which comes first in the
@@ -892,17 +893,21 @@ int FunctionComparator::cmpInlineAsm(const InlineAsm *L,
/// 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.
- if (L == FnL) {
- if (R == FnR)
- return 0;
- return -1;
- }
- if (R == FnR) {
- if (L == FnL)
- return 0;
- return 1;
+int FunctionComparator::cmpValues(const Value *L, const Value *R,
+ ValueComparisonKind Kind) const {
+ // A self-reference is equivalent only in structural contexts where the
+ // reference remains valid after forwarding-thunk merging. Otherwise,
+ // merging the functions can change the observable function pointer value.
+ if (Kind != ValueComparisonKind::Normal) {
+ const Value *StrippedL = L->stripPointerCasts();
+ const Value *StrippedR = R->stripPointerCasts();
+ if (StrippedL == FnL) {
+ if (StrippedR == FnR)
+ return 0;
+ return -1;
+ }
+ if (StrippedR == FnR)
+ return 1;
}
const Constant *ConstL = dyn_cast<Constant>(L);
@@ -961,11 +966,17 @@ int FunctionComparator::cmpBasicBlocks(const BasicBlock *BBL,
return Res;
if (needToCmpOperands) {
assert(InstL->getNumOperands() == InstR->getNumOperands());
+ const auto *CBL = dyn_cast<CallBase>(InstL);
+ const auto *CBR = dyn_cast<CallBase>(InstR);
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))
+ ValueComparisonKind Kind = ValueComparisonKind::Normal;
+ if (CBL && CBR && CBL->isCallee(&InstL->getOperandUse(i)) &&
+ CBR->isCallee(&InstR->getOperandUse(i)))
+ Kind = ValueComparisonKind::CallTarget;
+ if (int Res = cmpValues(OpL, OpR, Kind))
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..f7680a8a096cb
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/recursive-self-reference.ll
@@ -0,0 +1,25 @@
+; RUN: opt -S -passes=mergefunc < %s | FileCheck %s
+
+; Recursive call targets may still be considered equivalent.
+; CHECK-LABEL: define internal void @recursive_f(
+; CHECK: call void @recursive_f()
+; CHECK-NOT: @recursive_g
+; CHECK-LABEL: define i32 @main(
+; CHECK: call void @recursive_f()
+; CHECK: call void @recursive_f()
+
+define internal void @recursive_f() {
+ call void @recursive_f()
+ ret void
+}
+
+define internal void @recursive_g() {
+ call void @recursive_g()
+ ret void
+}
+
+define i32 @main() {
+ 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..2033be2073355
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/self-reference.ll
@@ -0,0 +1,50 @@
+; 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.
+; CHECK-LABEL: define void @f(
+; CHECK: icmp eq ptr {{.*}}, @f
+; CHECK: call void @llvm.assume
+; CHECK-LABEL: define void @g(
+; CHECK: icmp eq ptr {{.*}}, @g
+; CHECK-NOT: tail call void @f
+; CHECK: call void @llvm.assume
+
+define void @f(ptr %p) {
+ %cmp = icmp eq ptr %p, @f
+ call void @llvm.assume(i1 %cmp)
+ ret void
+}
+
+define void @g(ptr %p) {
+ %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.
+; CHECK-LABEL: define void @arg_f(
+; CHECK: call void @consume(ptr @arg_f)
+; CHECK-LABEL: define void @arg_g(
+; CHECK: call void @consume(ptr @arg_g)
+
+declare void @consume(ptr)
+
+define void @arg_f() {
+ call void @consume(ptr @arg_f)
+ ret void
+}
+
+define void @arg_g() {
+ call void @consume(ptr @arg_g)
+ ret void
+}
+
+define i32 @main() {
+ call void @f(ptr @f)
+ call void @g(ptr @g)
+ call void @arg_f()
+ call void @arg_g()
+ ret i32 0
+}
diff --git a/llvm/unittests/Transforms/Utils/FunctionComparatorTest.cpp b/llvm/unittests/Transforms/Utils/FunctionComparatorTest.cpp
index cd2b4e8046b2e..609c1ef036a28 100644
--- a/llvm/unittests/Transforms/Utils/FunctionComparatorTest.cpp
+++ b/llvm/unittests/Transforms/Utils/FunctionComparatorTest.cpp
@@ -6,12 +6,15 @@
//
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/FunctionComparator.h"
+#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
+#include "llvm/Support/SourceMgr.h"
#include "gtest/gtest.h"
+#include <memory>
using namespace llvm;
@@ -127,3 +130,46 @@ TEST(FunctionComparatorTest, TestAPI) {
EXPECT_EQ(Cmp.testCmpTypes(F1.T, F2.T), 0);
EXPECT_EQ(Cmp.testCmpPrimitives(), -4);
}
+
+TEST(FunctionComparatorTest, SelfReferenceComparisonContext) {
+ LLVMContext C;
+ SMDiagnostic Err;
+ std::unique_ptr<Module> M(parseAssemblyString(R"IR(
+ define void @f() {
+ call void @f()
+ ret void
+ }
+
+ define void @g() {
+ call void @g()
+ ret void
+ }
+
+ define void @observable_f(ptr %p) {
+ %cmp = icmp eq ptr %p, @observable_f
+ call void @llvm.assume(i1 %cmp)
+ ret void
+ }
+
+ define void @observable_g(ptr %p) {
+ %cmp = icmp eq ptr %p, @observable_g
+ call void @llvm.assume(i1 %cmp)
+ ret void
+ }
+
+ declare void @llvm.assume(i1)
+ )IR",
+ Err, C));
+ ASSERT_TRUE(M);
+
+ GlobalNumberState GN;
+ EXPECT_EQ(FunctionComparator(M->getFunction("f"), M->getFunction("g"), &GN)
+ .compare(),
+ 0);
+
+ GN.clear();
+ EXPECT_NE(FunctionComparator(M->getFunction("observable_f"),
+ M->getFunction("observable_g"), &GN)
+ .compare(),
+ 0);
+}
>From 130176a3ada70b53ae4e37485cc9934f12d98f4e Mon Sep 17 00:00:00 2001
From: Gaurav Chaudhary <chaudharygaurav2004 at gmail.com>
Date: Mon, 24 Aug 2026 11:19:20 +0530
Subject: [PATCH 2/2] [MergeFunc] Preserve observable function pointer identity
MergeFunc may replace one function with a forwarding thunk. Stop treating
every self-reference as equal in FunctionComparator; only call targets and
blockaddress may compare the functions under comparison as equal. Fixes
#213206
Signed-off-by: Gaurav Chaudhary <chaudharygaurav2004 at gmail.com>
---
.../Transforms/Utils/FunctionComparator.h | 30 +++++------
.../Transforms/Utils/FunctionComparator.cpp | 51 +++++++++----------
.../MergeFunc/recursive-self-reference.ll | 16 +++---
.../Transforms/MergeFunc/self-reference.ll | 39 ++++++++++----
4 files changed, 75 insertions(+), 61 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
index 0acf1e51d8cd1..1d2711d70ad27 100644
--- a/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
+++ b/llvm/include/llvm/Transforms/Utils/FunctionComparator.h
@@ -69,7 +69,7 @@ class GlobalNumberState {
public:
GlobalNumberState() = default;
- uint64_t getNumber(GlobalValue* Global) {
+ uint64_t getNumber(GlobalValue *Global) {
ValueNumberMap::iterator MapIter;
bool Inserted;
std::tie(MapIter, Inserted) = GlobalNumbers.insert({Global, NextNumber});
@@ -78,13 +78,9 @@ class GlobalNumberState {
return MapIter->second;
}
- void erase(GlobalValue *Global) {
- GlobalNumbers.erase(Global);
- }
+ void erase(GlobalValue *Global) { GlobalNumbers.erase(Global); }
- void clear() {
- GlobalNumbers.clear();
- }
+ void clear() { GlobalNumbers.clear(); }
};
/// FunctionComparator - Compares two functions to determine whether or not
@@ -94,15 +90,13 @@ class GlobalNumberState {
class FunctionComparator {
public:
FunctionComparator(const Function *F1, const Function *F2,
- GlobalNumberState* GN)
+ GlobalNumberState *GN)
: FnL(F1), FnR(F2), GlobalNumbers(GN) {}
/// Test whether the two functions have equivalent behaviour.
LLVM_ABI int compare();
protected:
- enum class ValueComparisonKind { Normal, CallTarget, BlockAddress };
-
/// Start the comparison.
void beginCompare() {
sn_mapL.clear();
@@ -228,9 +222,6 @@ class FunctionComparator {
/// return whether the numbers are equal. Numbers are assigned in the order
/// visited.
/// Comparison order:
- /// Stage 0: In CallTarget or BlockAddress comparison mode, references to the
- /// functions being compared are equal. In Normal comparison mode,
- /// function values are compared like other global values.
/// 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,9 +233,12 @@ class FunctionComparator {
/// then left value is greater.
/// In another words, we compare serial numbers, for more details
/// see comments for sn_mapL and sn_mapR.
- LLVM_ABI int cmpValues(
- const Value *L, const Value *R,
- ValueComparisonKind Kind = ValueComparisonKind::Normal) const;
+ 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.
@@ -387,10 +381,10 @@ class FunctionComparator {
/// But, we are still not able to compare operands of PHI nodes, since those
/// could be operands from further BBs we didn't scan yet.
/// So it's impossible to use dominance properties in general.
- mutable DenseMap<const Value*, int> sn_mapL, sn_mapR;
+ mutable DenseMap<const Value *, int> sn_mapL, sn_mapR;
// The global state we will use
- GlobalNumberState* GlobalNumbers;
+ GlobalNumberState *GlobalNumbers;
};
} // end namespace llvm
diff --git a/llvm/lib/Transforms/Utils/FunctionComparator.cpp b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
index 5f74719312b96..7bb8ddb484202 100644
--- a/llvm/lib/Transforms/Utils/FunctionComparator.cpp
+++ b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
@@ -478,8 +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(),
- ValueComparisonKind::BlockAddress))
+ 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
@@ -892,27 +892,23 @@ int FunctionComparator::cmpInlineAsm(const InlineAsm *L,
return 0;
}
+int FunctionComparator::cmpValuesAllowingSelfRef(const Value *L,
+ const Value *R) const {
+ if (L == FnL) {
+ if (R == FnR)
+ return 0;
+ return -1;
+ }
+ 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,
- ValueComparisonKind Kind) const {
- // A self-reference is equivalent only in structural contexts where the
- // reference remains valid after forwarding-thunk merging. Otherwise,
- // merging the functions can change the observable function pointer value.
- if (Kind != ValueComparisonKind::Normal) {
- const Value *StrippedL = L->stripPointerCasts();
- const Value *StrippedR = R->stripPointerCasts();
- if (StrippedL == FnL) {
- if (StrippedR == FnR)
- return 0;
- return -1;
- }
- if (StrippedR == FnR)
- return 1;
- }
-
+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) {
@@ -969,17 +965,20 @@ int FunctionComparator::cmpBasicBlocks(const BasicBlock *BBL,
return Res;
if (needToCmpOperands) {
assert(InstL->getNumOperands() == InstR->getNumOperands());
- const auto *CBL = dyn_cast<CallBase>(InstL);
- const auto *CBR = dyn_cast<CallBase>(InstR);
+ 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);
- ValueComparisonKind Kind = ValueComparisonKind::Normal;
- if (CBL && CBR && CBL->isCallee(&InstL->getOperandUse(i)) &&
- CBR->isCallee(&InstR->getOperandUse(i)))
- Kind = ValueComparisonKind::CallTarget;
- if (int Res = cmpValues(OpL, OpR, Kind))
+ int Res;
+ if (CBL && CBL->isCallee(&InstL->getOperandUse(i))) {
+ assert(isa<CallBase>(&*InstR) &&
+ 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
index f7680a8a096cb..7478696b43828 100644
--- a/llvm/test/Transforms/MergeFunc/recursive-self-reference.ll
+++ b/llvm/test/Transforms/MergeFunc/recursive-self-reference.ll
@@ -1,14 +1,13 @@
+; 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.
-; CHECK-LABEL: define internal void @recursive_f(
-; CHECK: call void @recursive_f()
-; CHECK-NOT: @recursive_g
-; CHECK-LABEL: define i32 @main(
-; CHECK: call void @recursive_f()
-; CHECK: call void @recursive_f()
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
}
@@ -19,6 +18,11 @@ define internal void @recursive_g() {
}
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
index 2033be2073355..99f6a87e44c0b 100644
--- a/llvm/test/Transforms/MergeFunc/self-reference.ll
+++ b/llvm/test/Transforms/MergeFunc/self-reference.ll
@@ -1,22 +1,28 @@
+; 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.
-; CHECK-LABEL: define void @f(
-; CHECK: icmp eq ptr {{.*}}, @f
-; CHECK: call void @llvm.assume
-; CHECK-LABEL: define void @g(
-; CHECK: icmp eq ptr {{.*}}, @g
-; CHECK-NOT: tail call void @f
-; CHECK: call void @llvm.assume
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
@@ -24,24 +30,35 @@ define void @g(ptr %p) {
; A self-reference passed as an argument is also observable and must remain a
; normal value comparison rather than a call-target comparison.
-; CHECK-LABEL: define void @arg_f(
-; CHECK: call void @consume(ptr @arg_f)
-; CHECK-LABEL: define void @arg_g(
-; CHECK: call void @consume(ptr @arg_g)
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()
More information about the llvm-commits
mailing list