[llvm] [Local][MergeFunc] Handle !callees when two calls are merged (PR #225307)
Mian Miftah via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 01:01:23 PDT 2026
https://github.com/mmiftahx updated https://github.com/llvm/llvm-project/pull/225307
>From 14aa56566780bb8df7c7bc6c5f833196b42c4cac Mon Sep 17 00:00:00 2001
From: mmiftahx <mmiftah.duna at gmail.com>
Date: Sun, 20 Sep 2026 17:01:21 -0500
Subject: [PATCH 1/2] [Local][MergeFunc] Handle !callees when two calls are
merged
When two calls are combined into one, combineMetadata drops !callees because
it does not know the metadata kind. That is safe, but it loses the callees
even if both calls list the same ones, e.g. when SimplifyCFG hoists a call
out of both arms of a branch.
Merge the two lists instead. A call that moves replaces the other call on
its path, so it may target any callee of either list, and its callees are
unknown if one of the calls does not list them. A call that stays in place,
as in CSE, keeps its own list. This follows what is done for !callee_type.
MergeFunctions has the opposite problem. It does not compare !callees, so it
merges two functions whose indirect calls list different callees, and the
body that is kept applies its own list to the callers of both. Compare the
metadata, as is done for !range. A listed function that was deleted leaves a
null operand behind, so make cmpMetadata accept null operands.
---
llvm/include/llvm/IR/Metadata.h | 1 +
llvm/lib/IR/Metadata.cpp | 12 ++
.../Transforms/Utils/FunctionComparator.cpp | 16 +-
llvm/lib/Transforms/Utils/Local.cpp | 6 +
llvm/test/Transforms/GVN/callees-metadata.ll | 31 ++++
.../Transforms/MergeFunc/callees-metadata.ll | 87 ++++++++++
.../SimplifyCFG/merge-callees-metadata.ll | 164 ++++++++++++++++++
7 files changed, 311 insertions(+), 6 deletions(-)
create mode 100644 llvm/test/Transforms/GVN/callees-metadata.ll
create mode 100644 llvm/test/Transforms/MergeFunc/callees-metadata.ll
create mode 100644 llvm/test/Transforms/SimplifyCFG/merge-callees-metadata.ll
diff --git a/llvm/include/llvm/IR/Metadata.h b/llvm/include/llvm/IR/Metadata.h
index 017fd98e704ea..9347a3dd93e13 100644
--- a/llvm/include/llvm/IR/Metadata.h
+++ b/llvm/include/llvm/IR/Metadata.h
@@ -1477,6 +1477,7 @@ class MDNode : public Metadata {
LLVM_ABI static MDNode *getMergedCallsiteMetadata(MDNode *A, MDNode *B);
LLVM_ABI static MDNode *getMergedCalleeTypeMetadata(const MDNode *A,
const MDNode *B);
+ LLVM_ABI static MDNode *getMergedCalleesMetadata(MDNode *A, MDNode *B);
LLVM_ABI static MDNode *getMergedAllocTokenMetadata(const MDNode *A,
const MDNode *B);
diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp
index 28873462748b2..a947bcfd6fa96 100644
--- a/llvm/lib/IR/Metadata.cpp
+++ b/llvm/lib/IR/Metadata.cpp
@@ -1332,6 +1332,18 @@ MDNode *MDNode::getMergedCalleeTypeMetadata(const MDNode *A, const MDNode *B) {
return MDNode::get(A->getContext(), AB);
}
+MDNode *MDNode::getMergedCalleesMetadata(MDNode *A, MDNode *B) {
+ // The callees of the merged call are unknown unless both calls list theirs.
+ if (!A || !B)
+ return nullptr;
+ if (A == B)
+ return A;
+ // The merged call may target any callee of either call.
+ SmallSetVector<Metadata *, 8> Callees(llvm::from_range, A->operands());
+ Callees.insert_range(B->operands());
+ return MDNode::get(A->getContext(), Callees.getArrayRef());
+}
+
MDNode *MDNode::getMergedAllocTokenMetadata(const MDNode *A, const MDNode *B) {
// Drop !alloc_token metadata if either instruction lacks it to avoid mis-
// classifying unclassified allocations, where the fallback token must be
diff --git a/llvm/lib/Transforms/Utils/FunctionComparator.cpp b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
index 9770ae4e39f66..415b56e019248 100644
--- a/llvm/lib/Transforms/Utils/FunctionComparator.cpp
+++ b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
@@ -193,8 +193,9 @@ int FunctionComparator::cmpMetadata(const Metadata *L,
// equal even though this is not correct.
// We should structurally compare the metadata nodes to be perfect here.
- auto *MDStringL = dyn_cast<MDString>(L);
- auto *MDStringR = dyn_cast<MDString>(R);
+ // An operand is null if the value it referred to was deleted.
+ auto *MDStringL = dyn_cast_if_present<MDString>(L);
+ auto *MDStringR = dyn_cast_if_present<MDString>(R);
if (MDStringL && MDStringR) {
if (MDStringL == MDStringR)
return 0;
@@ -205,8 +206,8 @@ int FunctionComparator::cmpMetadata(const Metadata *L,
if (MDStringL)
return 1;
- auto *CL = dyn_cast<ConstantAsMetadata>(L);
- auto *CR = dyn_cast<ConstantAsMetadata>(R);
+ auto *CL = dyn_cast_if_present<ConstantAsMetadata>(L);
+ auto *CR = dyn_cast_if_present<ConstantAsMetadata>(R);
if (CL == CR)
return 0;
if (!CL)
@@ -734,8 +735,11 @@ int FunctionComparator::cmpOperations(const Instruction *L,
if (int Res = cmpNumbers(CI->getTailCallKind(),
cast<CallInst>(R)->getTailCallKind()))
return Res;
- return cmpMDNode(L->getMetadata(LLVMContext::MD_range),
- R->getMetadata(LLVMContext::MD_range));
+ if (int Res = cmpMDNode(L->getMetadata(LLVMContext::MD_range),
+ R->getMetadata(LLVMContext::MD_range)))
+ return Res;
+ return cmpMDNode(L->getMetadata(LLVMContext::MD_callees),
+ R->getMetadata(LLVMContext::MD_callees));
}
if (const SwitchInst *SI = dyn_cast<SwitchInst>(L)) {
for (auto [LCase, RCase] : zip(SI->cases(), cast<SwitchInst>(R)->cases()))
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index 46c010e309449..25998047ad573 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -3020,6 +3020,12 @@ static void combineMetadata(Instruction *K, const Instruction *J,
MDNode::getMergedCalleeTypeMetadata(KMD, JMD));
}
break;
+ case LLVMContext::MD_callees:
+ // If K moves, it replaces J on J's path and must allow J's callees as
+ // well. If K does not move, its callees remain valid.
+ if (!AAOnly && DoesKMove)
+ K->setMetadata(Kind, MDNode::getMergedCalleesMetadata(KMD, JMD));
+ break;
case LLVMContext::MD_align:
if (!AAOnly && (DoesKMove || !K->hasMetadata(LLVMContext::MD_noundef)))
K->setMetadata(
diff --git a/llvm/test/Transforms/GVN/callees-metadata.ll b/llvm/test/Transforms/GVN/callees-metadata.ll
new file mode 100644
index 0000000000000..460b34c0f588f
--- /dev/null
+++ b/llvm/test/Transforms/GVN/callees-metadata.ll
@@ -0,0 +1,31 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 5
+; RUN: opt -passes=gvn -S < %s | FileCheck %s
+
+; The first call stays where it is when it replaces the second one, so its
+; callees remain valid. The lists overlap so their union and intersection
+; both differ from the retained list.
+
+declare i32 @a()
+declare i32 @b()
+declare i32 @c()
+
+define i32 @cse(ptr %fp) {
+; CHECK-LABEL: define i32 @cse(
+; CHECK-SAME: ptr [[FP:%.*]]) {
+; CHECK-NEXT: [[X:%.*]] = call i32 [[FP]]() #[[ATTR0:[0-9]+]], !callees [[META0:![0-9]+]]
+; CHECK-NEXT: [[SUM:%.*]] = add i32 [[X]], [[X]]
+; CHECK-NEXT: ret i32 [[SUM]]
+;
+ %x = call i32 %fp() memory(none), !callees !0
+ %y = call i32 %fp() memory(none), !callees !1
+ %sum = add i32 %x, %y
+ ret i32 %sum
+}
+
+!0 = !{ptr @a, ptr @b}
+!1 = !{ptr @b, ptr @c}
+;.
+; CHECK: attributes #[[ATTR0]] = { memory(none) }
+;.
+; CHECK: [[META0]] = !{ptr @a, ptr @b}
+;.
diff --git a/llvm/test/Transforms/MergeFunc/callees-metadata.ll b/llvm/test/Transforms/MergeFunc/callees-metadata.ll
new file mode 100644
index 0000000000000..7621aab99adc8
--- /dev/null
+++ b/llvm/test/Transforms/MergeFunc/callees-metadata.ll
@@ -0,0 +1,87 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --include-generated-funcs --version 5
+; RUN: opt -passes=mergefunc -S < %s | FileCheck %s --implicit-check-not='!callees'
+
+; Merging two functions keeps the body of one of them. If their indirect calls
+; allow different callees, that body would apply its own callee set to the
+; callers of both functions.
+
+declare void @a()
+declare void @b()
+
+define void @calls_a(ptr %fp) {
+ call void %fp(), !callees !0
+ ret void
+}
+
+define void @calls_b(ptr %fp) {
+ call void %fp(), !callees !1
+ ret void
+}
+
+define void @calls_unknown(ptr %fp) {
+ call void %fp()
+ ret void
+}
+
+; Identical callee lists still allow merging.
+define void @also_calls_a(ptr %fp) {
+ call void %fp(), !callees !0
+ ret void
+}
+
+; A null operand is left behind when a listed function is deleted.
+define void @deleted_first(ptr %fp) {
+ call void %fp(), !callees !2
+ ret void
+}
+
+define void @deleted_last(ptr %fp) {
+ call void %fp(), !callees !3
+ ret void
+}
+
+!0 = !{ptr @a}
+!1 = !{ptr @b}
+!2 = !{null, ptr @a}
+!3 = !{ptr @a, null}
+; CHECK-LABEL: define void @calls_b(
+; CHECK-SAME: ptr [[FP:%.*]]) {
+; CHECK-NEXT: call void [[FP]](), !callees [[META0:![0-9]+]]
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define void @calls_unknown(
+; CHECK-SAME: ptr [[FP:%.*]]) {
+; CHECK-NEXT: call void [[FP]]()
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define void @also_calls_a(
+; CHECK-SAME: ptr [[FP:%.*]]) {
+; CHECK-NEXT: call void [[FP]](), !callees [[META1:![0-9]+]]
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define void @deleted_first(
+; CHECK-SAME: ptr [[FP:%.*]]) {
+; CHECK-NEXT: call void [[FP]](), !callees [[META2:![0-9]+]]
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define void @deleted_last(
+; CHECK-SAME: ptr [[FP:%.*]]) {
+; CHECK-NEXT: call void [[FP]](), !callees [[META3:![0-9]+]]
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define void @calls_a(
+; CHECK-SAME: ptr [[TMP0:%.*]]) {
+; CHECK-NEXT: tail call void @also_calls_a(ptr [[TMP0]])
+; CHECK-NEXT: ret void
+;
+;.
+; CHECK: [[META0]] = !{ptr @b}
+; CHECK: [[META1]] = !{ptr @a}
+; CHECK: [[META2]] = !{null, ptr @a}
+; CHECK: [[META3]] = !{ptr @a, null}
+;.
diff --git a/llvm/test/Transforms/SimplifyCFG/merge-callees-metadata.ll b/llvm/test/Transforms/SimplifyCFG/merge-callees-metadata.ll
new file mode 100644
index 0000000000000..bc5a5f23a18a8
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/merge-callees-metadata.ll
@@ -0,0 +1,164 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 5
+; RUN: opt -passes='simplifycfg<hoist-common-insts;sink-common-insts>' -S < %s | FileCheck %s --implicit-check-not='!callees'
+
+; Hoisting or sinking replaces the calls on both paths with a single call,
+; which must allow the callees of either of them.
+
+declare void @a()
+declare void @b()
+declare void @c()
+declare void @use(i32)
+
+define void @same_set(i1 %cond, ptr %fp) {
+; CHECK-LABEL: define void @same_set(
+; CHECK-SAME: i1 [[COND:%.*]], ptr [[FP:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: call void [[FP]](), !callees [[META0:![0-9]+]]
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %cond, label %then, label %else
+
+then:
+ call void %fp(), !callees !0
+ br label %end
+
+else:
+ call void %fp(), !callees !0
+ br label %end
+
+end:
+ ret void
+}
+
+define void @different_sets(i1 %cond, ptr %fp) {
+; CHECK-LABEL: define void @different_sets(
+; CHECK-SAME: i1 [[COND:%.*]], ptr [[FP:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: call void [[FP]](), !callees [[META1:![0-9]+]]
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %cond, label %then, label %else
+
+then:
+ call void %fp(), !callees !0
+ br label %end
+
+else:
+ call void %fp(), !callees !1
+ br label %end
+
+end:
+ ret void
+}
+
+; Sinking merges calls through different pointers into a call through a select
+; of them, which may target the callees of both.
+define void @different_pointers(i1 %cond, ptr %fp1, ptr %fp2) {
+; CHECK-LABEL: define void @different_pointers(
+; CHECK-SAME: i1 [[COND:%.*]], ptr [[FP1:%.*]], ptr [[FP2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[DOT:%.*]] = select i1 [[COND]], i32 1, i32 2
+; CHECK-NEXT: [[FP1_FP2:%.*]] = select i1 [[COND]], ptr [[FP1]], ptr [[FP2]]
+; CHECK-NEXT: call void @use(i32 [[DOT]])
+; CHECK-NEXT: call void [[FP1_FP2]](), !callees [[META2:![0-9]+]]
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %cond, label %then, label %else
+
+then:
+ call void @use(i32 1)
+ call void %fp1(), !callees !3
+ br label %end
+
+else:
+ call void @use(i32 2)
+ call void %fp2(), !callees !4
+ br label %end
+
+end:
+ ret void
+}
+
+; The callees of the merged call are unknown if either call does not list them.
+define void @only_then_has_set(i1 %cond, ptr %fp) {
+; CHECK-LABEL: define void @only_then_has_set(
+; CHECK-SAME: i1 [[COND:%.*]], ptr [[FP:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: call void [[FP]]()
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %cond, label %then, label %else
+
+then:
+ call void %fp(), !callees !0
+ br label %end
+
+else:
+ call void %fp()
+ br label %end
+
+end:
+ ret void
+}
+
+define void @only_else_has_set(i1 %cond, ptr %fp) {
+; CHECK-LABEL: define void @only_else_has_set(
+; CHECK-SAME: i1 [[COND:%.*]], ptr [[FP:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: call void [[FP]]()
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %cond, label %then, label %else
+
+then:
+ call void %fp()
+ br label %end
+
+else:
+ call void %fp(), !callees !0
+ br label %end
+
+end:
+ ret void
+}
+
+; A null operand is left behind when a listed function is deleted. Preserve
+; it when merging the lists.
+define void @deleted_callee(i1 %cond, ptr %fp) {
+; CHECK-LABEL: define void @deleted_callee(
+; CHECK-SAME: i1 [[COND:%.*]], ptr [[FP:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: call void [[FP]](), !callees [[META3:![0-9]+]]
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %cond, label %then, label %else
+
+then:
+ call void %fp(), !callees !2
+ br label %end
+
+else:
+ call void %fp(), !callees !1
+ br label %end
+
+end:
+ ret void
+}
+
+!0 = !{ptr @a, ptr @b}
+!1 = !{ptr @b, ptr @c}
+!2 = !{ptr @a, null}
+!3 = !{ptr @a}
+!4 = !{ptr @c}
+;.
+; CHECK: [[META0]] = !{ptr @a, ptr @b}
+; CHECK: [[META1]] = !{ptr @a, ptr @b, ptr @c}
+; CHECK: [[META2]] = !{ptr @c, ptr @a}
+; CHECK: [[META3]] = !{ptr @a, null, ptr @b, ptr @c}
+;.
>From afad1d405d35a6414ebacc05b10dc1072d40a86c Mon Sep 17 00:00:00 2001
From: mmiftahx <mmiftah.duna at gmail.com>
Date: Tue, 22 Sep 2026 03:01:04 -0500
Subject: [PATCH 2/2] fixup! [Local][MergeFunc] Handle !callees when two calls
are merged
---
.../Transforms/Utils/FunctionComparator.cpp | 16 ++--
.../Transforms/MergeFunc/callees-metadata.ll | 87 -------------------
2 files changed, 6 insertions(+), 97 deletions(-)
delete mode 100644 llvm/test/Transforms/MergeFunc/callees-metadata.ll
diff --git a/llvm/lib/Transforms/Utils/FunctionComparator.cpp b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
index 415b56e019248..9770ae4e39f66 100644
--- a/llvm/lib/Transforms/Utils/FunctionComparator.cpp
+++ b/llvm/lib/Transforms/Utils/FunctionComparator.cpp
@@ -193,9 +193,8 @@ int FunctionComparator::cmpMetadata(const Metadata *L,
// equal even though this is not correct.
// We should structurally compare the metadata nodes to be perfect here.
- // An operand is null if the value it referred to was deleted.
- auto *MDStringL = dyn_cast_if_present<MDString>(L);
- auto *MDStringR = dyn_cast_if_present<MDString>(R);
+ auto *MDStringL = dyn_cast<MDString>(L);
+ auto *MDStringR = dyn_cast<MDString>(R);
if (MDStringL && MDStringR) {
if (MDStringL == MDStringR)
return 0;
@@ -206,8 +205,8 @@ int FunctionComparator::cmpMetadata(const Metadata *L,
if (MDStringL)
return 1;
- auto *CL = dyn_cast_if_present<ConstantAsMetadata>(L);
- auto *CR = dyn_cast_if_present<ConstantAsMetadata>(R);
+ auto *CL = dyn_cast<ConstantAsMetadata>(L);
+ auto *CR = dyn_cast<ConstantAsMetadata>(R);
if (CL == CR)
return 0;
if (!CL)
@@ -735,11 +734,8 @@ int FunctionComparator::cmpOperations(const Instruction *L,
if (int Res = cmpNumbers(CI->getTailCallKind(),
cast<CallInst>(R)->getTailCallKind()))
return Res;
- if (int Res = cmpMDNode(L->getMetadata(LLVMContext::MD_range),
- R->getMetadata(LLVMContext::MD_range)))
- return Res;
- return cmpMDNode(L->getMetadata(LLVMContext::MD_callees),
- R->getMetadata(LLVMContext::MD_callees));
+ return cmpMDNode(L->getMetadata(LLVMContext::MD_range),
+ R->getMetadata(LLVMContext::MD_range));
}
if (const SwitchInst *SI = dyn_cast<SwitchInst>(L)) {
for (auto [LCase, RCase] : zip(SI->cases(), cast<SwitchInst>(R)->cases()))
diff --git a/llvm/test/Transforms/MergeFunc/callees-metadata.ll b/llvm/test/Transforms/MergeFunc/callees-metadata.ll
deleted file mode 100644
index 7621aab99adc8..0000000000000
--- a/llvm/test/Transforms/MergeFunc/callees-metadata.ll
+++ /dev/null
@@ -1,87 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --include-generated-funcs --version 5
-; RUN: opt -passes=mergefunc -S < %s | FileCheck %s --implicit-check-not='!callees'
-
-; Merging two functions keeps the body of one of them. If their indirect calls
-; allow different callees, that body would apply its own callee set to the
-; callers of both functions.
-
-declare void @a()
-declare void @b()
-
-define void @calls_a(ptr %fp) {
- call void %fp(), !callees !0
- ret void
-}
-
-define void @calls_b(ptr %fp) {
- call void %fp(), !callees !1
- ret void
-}
-
-define void @calls_unknown(ptr %fp) {
- call void %fp()
- ret void
-}
-
-; Identical callee lists still allow merging.
-define void @also_calls_a(ptr %fp) {
- call void %fp(), !callees !0
- ret void
-}
-
-; A null operand is left behind when a listed function is deleted.
-define void @deleted_first(ptr %fp) {
- call void %fp(), !callees !2
- ret void
-}
-
-define void @deleted_last(ptr %fp) {
- call void %fp(), !callees !3
- ret void
-}
-
-!0 = !{ptr @a}
-!1 = !{ptr @b}
-!2 = !{null, ptr @a}
-!3 = !{ptr @a, null}
-; CHECK-LABEL: define void @calls_b(
-; CHECK-SAME: ptr [[FP:%.*]]) {
-; CHECK-NEXT: call void [[FP]](), !callees [[META0:![0-9]+]]
-; CHECK-NEXT: ret void
-;
-;
-; CHECK-LABEL: define void @calls_unknown(
-; CHECK-SAME: ptr [[FP:%.*]]) {
-; CHECK-NEXT: call void [[FP]]()
-; CHECK-NEXT: ret void
-;
-;
-; CHECK-LABEL: define void @also_calls_a(
-; CHECK-SAME: ptr [[FP:%.*]]) {
-; CHECK-NEXT: call void [[FP]](), !callees [[META1:![0-9]+]]
-; CHECK-NEXT: ret void
-;
-;
-; CHECK-LABEL: define void @deleted_first(
-; CHECK-SAME: ptr [[FP:%.*]]) {
-; CHECK-NEXT: call void [[FP]](), !callees [[META2:![0-9]+]]
-; CHECK-NEXT: ret void
-;
-;
-; CHECK-LABEL: define void @deleted_last(
-; CHECK-SAME: ptr [[FP:%.*]]) {
-; CHECK-NEXT: call void [[FP]](), !callees [[META3:![0-9]+]]
-; CHECK-NEXT: ret void
-;
-;
-; CHECK-LABEL: define void @calls_a(
-; CHECK-SAME: ptr [[TMP0:%.*]]) {
-; CHECK-NEXT: tail call void @also_calls_a(ptr [[TMP0]])
-; CHECK-NEXT: ret void
-;
-;.
-; CHECK: [[META0]] = !{ptr @b}
-; CHECK: [[META1]] = !{ptr @a}
-; CHECK: [[META2]] = !{null, ptr @a}
-; CHECK: [[META3]] = !{ptr @a, null}
-;.
More information about the llvm-commits
mailing list