[llvm] [IR] Define empty and malformed !callees semantics (PR #221550)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 6 02:51:09 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-amdgpu
Author: MM (mmiftahx)
<details>
<summary>Changes</summary>
`!callees` constrains the defined targets of an indirect call, but the LangRef
does not currently define the empty case or specify how malformed attachments
must be handled. Existing consumers consequently decode the metadata
differently, and some can consume the valid-looking prefix of a malformed
node.
This patch defines the following behavior:
* The operands form an exhaustive list of possible callees. The list may
contain dynamically infeasible candidates, and operand order and duplication
are not semantically significant.
* An empty node is an exhaustive empty list. Executing the call is undefined.
* Missing metadata provides no target information.
* Metadata attached to a non-indirect call provides no target information.
* If any operand does not refer to a `Function`, the complete attachment is
ignored rather than partially consumed.
The malformed rule includes null operands left when a function referenced only
through metadata is deleted.
`CallBase::getCalleesMetadata` provides a common fail-closed decoder. It
distinguishes an empty list from absence, removes duplicate functions while
preserving first-occurrence order, and clears its result on failure.
Module summary analysis, Attributor, and AMDGPU module splitting use the common
decoder. Attributor also preserves frontend-provided targets when pointer flow
leaves and later re-enters the IR module. Lack of a visible in-module path is
not proof that a listed target is impossible.
Tests cover absent, empty, duplicate, malformed, deleted-function, direct-call,
inline-assembly, call, and invoke cases; Attributor empty-set and external-flow
behavior; ThinLTO summary construction; and AMDGPU module splitting.
Validation: built opt, llvm-lto2, llvm-split, and IRTests in Release with
assertions for AMDGPU and X86. All four focused lit tests and both
InstructionsTest.CalleesMetadata* unit tests passed.
---
Patch is 22.10 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/221550.diff
13 Files Affected:
- (modified) llvm/docs/LangRef.md (+15-6)
- (modified) llvm/include/llvm/IR/InstrTypes.h (+9)
- (modified) llvm/include/llvm/IR/MDBuilder.h (+2-2)
- (modified) llvm/lib/Analysis/ModuleSummaryAnalysis.cpp (+7-10)
- (modified) llvm/lib/IR/Instructions.cpp (+23)
- (modified) llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp (+4-24)
- (modified) llvm/lib/Transforms/IPO/AttributorAttributes.cpp (+15-7)
- (added) llvm/test/ThinLTO/X86/Inputs/callees-metadata-malformed.ll (+13)
- (added) llvm/test/ThinLTO/X86/callees-metadata-malformed.ll (+23)
- (added) llvm/test/Transforms/Attributor/callees-metadata-external-roundtrip.ll (+25)
- (added) llvm/test/Transforms/Attributor/callees-metadata-malformed.ll (+33)
- (modified) llvm/test/tools/llvm-split/AMDGPU/kernels-dependency-indirect-callee-md.ll (+11)
- (modified) llvm/unittests/IR/InstructionsTest.cpp (+134)
``````````diff
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index 14caff88243c1..0cfc15a681e56 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -7778,12 +7778,21 @@ Example (assuming 64-bit pointers):
#### '`callees`' Metadata
-`callees` metadata may be attached to indirect call sites. If `callees`
-metadata is attached to a call site, and any callee is not among the set of
-functions provided by the metadata, the behavior is undefined. The intent of
-this metadata is to facilitate optimizations such as indirect-call promotion.
-For example, in the code below, the call instruction may only target the
-`add` or `sub` functions:
+`callees` metadata may be attached to indirect call sites.
+Its operands provide an exhaustive list of possible callees.
+The list may be conservative: a listed function need not be dynamically feasible, but on every defined execution of the call the callee must be one of the listed functions.
+The order and duplication of operands are semantically irrelevant.
+The intent of this metadata is to facilitate optimizations such as indirect-call promotion.
+
+An empty node is an exhaustive empty set, so executing the call has undefined behavior.
+If the metadata is absent, no exhaustive callee information is provided.
+
+Each operand must refer to a `Function`.
+If any operand does not, the entire attachment provides no information and must be ignored.
+This includes null operands left behind when a function referenced only through metadata is deleted.
+An attachment on a non-indirect call likewise provides no information.
+
+For example, in the code below, the call instruction may only target the `add` or `sub` functions:
```llvm
%result = call i64 %binop(i64 %x, i64 %y), !callees !0
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 5f7df6a4eb6f8..b5a0932069a4c 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -1424,6 +1424,15 @@ class CallBase : public Instruction {
/// Return true if the callsite is an indirect call.
LLVM_ABI bool isIndirectCall() const;
+ /// Decode the exhaustive list of possible callees from !callees metadata.
+ ///
+ /// Return true for a well-formed attachment on an indirect call, including
+ /// an empty attachment. Return false if the attachment is absent, is on a
+ /// non-indirect call, or contains an operand that is not a Function.
+ /// \p Callees is cleared on failure. On success, duplicate functions are
+ /// omitted while preserving the order of their first occurrence.
+ LLVM_ABI bool getCalleesMetadata(SmallVectorImpl<Function *> &Callees) const;
+
/// Determine whether the passed iterator points to the callee operand's Use.
bool isCallee(Value::const_user_iterator UI) const {
return isCallee(&UI.getUse());
diff --git a/llvm/include/llvm/IR/MDBuilder.h b/llvm/include/llvm/IR/MDBuilder.h
index e88de73567c35..1db8007e40598 100644
--- a/llvm/include/llvm/IR/MDBuilder.h
+++ b/llvm/include/llvm/IR/MDBuilder.h
@@ -131,8 +131,8 @@ class MDBuilder {
// Callees metadata.
//===------------------------------------------------------------------===//
- /// Return metadata indicating the possible callees of indirect
- /// calls.
+ /// Return metadata indicating the exhaustive list of possible callees of
+ /// indirect calls.
LLVM_ABI MDNode *createCallees(ArrayRef<Function *> Callees);
//===------------------------------------------------------------------===//
diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
index bb5fd4eefb7f9..0c69032fc3adc 100644
--- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
+++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
@@ -512,17 +512,14 @@ static void computeFunctionSummary(
if (!CalledValue || isa<Constant>(CalledValue))
continue;
- // Check if the instruction has a callees metadata. If so, add callees
- // to CallGraphEdges to reflect the references from the metadata, and
- // to enable importing for subsequent indirect call promotion and
+ // If the instruction has valid callees metadata, add its callees to
+ // CallGraphEdges to reflect the references from the metadata, and to
+ // enable importing for subsequent indirect call promotion and
// inlining.
- if (auto *MD = I.getMetadata(LLVMContext::MD_callees)) {
- for (const auto &Op : MD->operands()) {
- Function *Callee = mdconst::extract_or_null<Function>(Op);
- if (Callee)
- CallGraphEdges[Index.getOrInsertValueInfo(Callee)];
- }
- }
+ SmallVector<Function *, 4> Callees;
+ if (CB->getCalleesMetadata(Callees))
+ for (Function *Callee : Callees)
+ CallGraphEdges[Index.getOrInsertValueInfo(Callee)];
CandidateProfileData =
ICallAnalysis.getPromotionCandidatesForInstruction(
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index 325850b0a880d..b535eb7a4a680 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -14,6 +14,7 @@
#include "llvm/IR/Instructions.h"
#include "LLVMContextImpl.h"
#include "llvm/ADT/SmallBitVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
#include "llvm/IR/Attributes.h"
@@ -341,6 +342,28 @@ bool CallBase::isIndirectCall() const {
return !isInlineAsm();
}
+bool CallBase::getCalleesMetadata(SmallVectorImpl<Function *> &Callees) const {
+ Callees.clear();
+ if (!isIndirectCall())
+ return false;
+
+ const MDNode *MD = getMetadata(LLVMContext::MD_callees);
+ if (!MD)
+ return false;
+
+ SmallPtrSet<Function *, 4> Seen;
+ for (const MDOperand &Op : MD->operands()) {
+ Function *Callee = mdconst::dyn_extract_or_null<Function>(Op);
+ if (!Callee) {
+ Callees.clear();
+ return false;
+ }
+ if (Seen.insert(Callee).second)
+ Callees.push_back(Callee);
+ }
+ return true;
+}
+
/// Tests if this call site must be tail call optimized. Only a CallInst can
/// be tail call optimized.
bool CallBase::isMustTailCall() const {
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp b/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp
index 05857d88a597c..ba5929736d5d4 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUSplitModule.cpp
@@ -475,29 +475,6 @@ void SplitGraph::Node::visitAllDependencies(
}
}
-/// Checks if \p I has MD_callees and if it does, parse it and put the function
-/// in \p Callees.
-///
-/// \returns true if there was metadata and it was parsed correctly. false if
-/// there was no MD or if it contained unknown entries and parsing failed.
-/// If this returns false, \p Callees will contain incomplete information
-/// and must not be used.
-static bool handleCalleesMD(const Instruction &I,
- SetVector<Function *> &Callees) {
- auto *MD = I.getMetadata(LLVMContext::MD_callees);
- if (!MD)
- return false;
-
- for (const auto &Op : MD->operands()) {
- Function *Callee = mdconst::extract_or_null<Function>(Op);
- if (!Callee)
- return false;
- Callees.insert(Callee);
- }
-
- return true;
-}
-
void SplitGraph::buildGraph(CallGraph &CG) {
SplitModuleTimer SMT("buildGraph", "graph construction");
LLVM_DEBUG(
@@ -550,8 +527,11 @@ void SplitGraph::buildGraph(CallGraph &CG) {
continue;
}
- if (handleCalleesMD(Inst, KnownCallees))
+ SmallVector<Function *, 4> CallCallees;
+ if (CB->getCalleesMetadata(CallCallees)) {
+ KnownCallees.insert_range(CallCallees);
continue;
+ }
// If we failed to parse any !callees MD, or some was missing,
// the entire KnownCallees list is now unreliable.
KnownCallees.clear();
diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 8d16f49525ca8..e9df83bbc8c5d 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -12372,14 +12372,13 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
/// See AbstractAttribute::initialize(...).
void initialize(Attributor &A) override {
- auto *MD = getCtxI()->getMetadata(LLVMContext::MD_callees);
- if (!MD && !A.isClosedWorldModule())
+ SmallVector<Function *, 4> Callees;
+ HasCalleesMetadata = cast<CallBase>(getCtxI())->getCalleesMetadata(Callees);
+ if (!HasCalleesMetadata && !A.isClosedWorldModule())
return;
- if (MD) {
- for (const auto &Op : MD->operands())
- if (Function *Callee = mdconst::dyn_extract_or_null<Function>(Op))
- PotentialCallees.insert(Callee);
+ if (HasCalleesMetadata) {
+ PotentialCallees.insert_range(Callees);
} else if (A.isClosedWorldModule()) {
ArrayRef<Function *> IndirectlyCallableFunctions =
A.getInfoCache().getIndirectlyCallableFunctions(A);
@@ -12400,6 +12399,11 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
auto CheckPotentialCalleeUse = [&](Function &PotentialCallee,
bool &UsedAssumedInformation) {
+ // A semantic !callees list can describe pointer flow through code
+ // outside this IR module. Do not remove a listed target merely because
+ // AAGlobalValueInfo cannot find an in-module path to the call operand.
+ if (HasCalleesMetadata)
+ return true;
const auto *GIAA = A.getAAFor<AAGlobalValueInfo>(
*this, IRPosition::value(PotentialCallee), DepClassTy::OPTIONAL);
if (!GIAA || GIAA->isPotentialUse(CalleeUse))
@@ -12674,10 +12678,14 @@ struct AAIndirectCallInfoCallSite : public AAIndirectCallInfo {
/// Map to remember filter results.
DenseMap<Function *, std::optional<bool>> FilterResults;
- /// If the !callee metadata was present, this set will contain all potential
+ /// If !callees metadata was present, this set will contain all potential
/// callees (superset).
SmallSetVector<Function *, 4> PotentialCallees;
+ /// Whether PotentialCallees came from semantic !callees metadata rather than
+ /// the Attributor's closed-world candidate inventory.
+ bool HasCalleesMetadata = false;
+
/// This set contains all currently assumed calllees, which might grow over
/// time.
SmallSetVector<Function *, 4> AssumedCallees;
diff --git a/llvm/test/ThinLTO/X86/Inputs/callees-metadata-malformed.ll b/llvm/test/ThinLTO/X86/Inputs/callees-metadata-malformed.ll
new file mode 100644
index 0000000000000..568cdb021e0f9
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/Inputs/callees-metadata-malformed.ll
@@ -0,0 +1,13 @@
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @callee(ptr %target) {
+ call void %target(), !callees !0
+ ret void
+}
+
+define internal void @metadata_only_target() {
+ ret void
+}
+
+!0 = !{ptr @metadata_only_target, ptr null}
diff --git a/llvm/test/ThinLTO/X86/callees-metadata-malformed.ll b/llvm/test/ThinLTO/X86/callees-metadata-malformed.ll
new file mode 100644
index 0000000000000..beed8e19d79e6
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/callees-metadata-malformed.ll
@@ -0,0 +1,23 @@
+; RUN: opt -module-summary %s -o %t1.bc
+; RUN: opt -module-summary %p/Inputs/callees-metadata-malformed.ll -o %t2.bc
+; RUN: llvm-lto2 run %t1.bc %t2.bc -o %t.o -save-temps \
+; RUN: -r=%t1.bc,caller,plx \
+; RUN: -r=%t1.bc,callee,l \
+; RUN: -r=%t2.bc,callee,pl
+; RUN: llvm-dis %t.o.1.3.import.bc -o - | FileCheck %s
+
+; A mixed malformed !callees node must not add an edge for its valid-looking
+; operand to the module summary.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; CHECK: define available_externally void @callee
+; CHECK-NOT: define {{.*}} @metadata_only_target
+
+define void @caller(ptr %target) {
+ call void @callee(ptr %target)
+ ret void
+}
+
+declare void @callee(ptr)
diff --git a/llvm/test/Transforms/Attributor/callees-metadata-external-roundtrip.ll b/llvm/test/Transforms/Attributor/callees-metadata-external-roundtrip.ll
new file mode 100644
index 0000000000000..93630b6cb7b26
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/callees-metadata-external-roundtrip.ll
@@ -0,0 +1,25 @@
+; RUN: opt -passes=attributor -S %s | FileCheck %s
+
+target triple = "amdgcn-amd-amdhsa"
+
+; The host may read @h and later pass or store that pointer for @k. Semantic
+; !callees is the authority for this external round trip; the absence of an
+; in-module use path from @f to %p cannot erase the call and its volatile side
+; effect.
+ at h = protected constant ptr @f
+ at sink = protected global i32 0
+ at llvm.used = appending global [1 x ptr] [ptr @h], section "llvm.metadata"
+
+define hidden void @f() noinline {
+ store volatile i32 1, ptr @sink
+ ret void
+}
+
+define amdgpu_kernel void @k(ptr %p) {
+; CHECK-LABEL: define amdgpu_kernel void @k(
+; CHECK: call void @f()
+ call void %p(), !callees !0
+ ret void
+}
+
+!0 = !{ptr @f}
diff --git a/llvm/test/Transforms/Attributor/callees-metadata-malformed.ll b/llvm/test/Transforms/Attributor/callees-metadata-malformed.ll
new file mode 100644
index 0000000000000..8783a842abdd5
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/callees-metadata-malformed.ll
@@ -0,0 +1,33 @@
+; RUN: opt -passes='globaldce,attributor' -S %s | FileCheck %s
+
+; A function referenced only by metadata can be deleted, leaving a raw null
+; operand behind. The resulting malformed !callees attachment must be ignored
+; rather than interpreted as an exhaustive empty set.
+
+define void @caller(ptr %callee) {
+; CHECK-LABEL: define void @caller(
+; CHECK: call void %callee(), !callees ![[CALLEES:[0-9]+]]
+; CHECK-NEXT: ret void
+ call void %callee(), !callees !0
+ ret void
+}
+
+define internal void @metadata_only_target() {
+ ret void
+}
+
+; CHECK-NOT: define internal void @metadata_only_target
+
+; A well-formed empty attachment is an exhaustive empty set. Reaching the call
+; is therefore undefined, and Attributor may make the call site unreachable.
+define void @empty_callees(ptr %callee) {
+; CHECK-LABEL: define void @empty_callees(
+; CHECK-NEXT: unreachable
+ call void %callee(), !callees !1
+ ret void
+}
+
+; CHECK: ![[CALLEES]] = distinct !{null}
+
+!0 = !{ptr @metadata_only_target}
+!1 = !{}
diff --git a/llvm/test/tools/llvm-split/AMDGPU/kernels-dependency-indirect-callee-md.ll b/llvm/test/tools/llvm-split/AMDGPU/kernels-dependency-indirect-callee-md.ll
index e1a8e5fe40a07..e170b7cb1d37f 100644
--- a/llvm/test/tools/llvm-split/AMDGPU/kernels-dependency-indirect-callee-md.ll
+++ b/llvm/test/tools/llvm-split/AMDGPU/kernels-dependency-indirect-callee-md.ll
@@ -8,6 +8,17 @@
; RUN: llvm-dis -o - %t-nomd1 | FileCheck --check-prefix=CHECK-NOMD1 --implicit-check-not=define %s
; RUN: llvm-dis -o - %t-nomd2 | FileCheck --check-prefix=CHECK-NOMD2 --implicit-check-not=define %s
+; A malformed attachment must behave like an absent attachment. In particular,
+; do not retain the valid-looking prefix of a mixed node.
+; RUN: sed 's/_MD_/, !callees !{ptr @CallCandidate0, ptr null}/g' %s | \
+; RUN: llvm-split -o %t-malformed -j 3 -mtriple amdgpu-amd-amdhsa
+; RUN: llvm-dis -o - %t-malformed0 | \
+; RUN: FileCheck --check-prefix=CHECK-NOMD0 --implicit-check-not=define %s
+; RUN: llvm-dis -o - %t-malformed1 | \
+; RUN: FileCheck --check-prefix=CHECK-NOMD1 --implicit-check-not=define %s
+; RUN: llvm-dis -o - %t-malformed2 | \
+; RUN: FileCheck --check-prefix=CHECK-NOMD2 --implicit-check-not=define %s
+
; CHECK0: define internal void @HelperC
; CHECK0: define amdgpu_kernel void @C
diff --git a/llvm/unittests/IR/InstructionsTest.cpp b/llvm/unittests/IR/InstructionsTest.cpp
index 5f92f325f793f..900dbb00e26e2 100644
--- a/llvm/unittests/IR/InstructionsTest.cpp
+++ b/llvm/unittests/IR/InstructionsTest.cpp
@@ -20,7 +20,11 @@
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/FPEnv.h"
#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalAlias.h"
+#include "llvm/IR/GlobalIFunc.h"
+#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Module.h"
@@ -130,6 +134,136 @@ TEST_F(ModuleWithFunctionTest, InvokeInst) {
}
}
+TEST(InstructionsTest, CalleesMetadataDecoding) {
+ LLVMContext C;
+ Module M("test", C);
+ FunctionType *CalleeTy = FunctionType::get(Type::getVoidTy(C), false);
+ Function *Target0 =
+ Function::Create(CalleeTy, GlobalValue::ExternalLinkage, "target0", M);
+ Function *Target1 =
+ Function::Create(CalleeTy, GlobalValue::ExternalLinkage, "target1", M);
+
+ FunctionType *CallerTy =
+ FunctionType::get(Type::getVoidTy(C), PointerType::getUnqual(C), false);
+ Function *Caller =
+ Function::Create(CallerTy, GlobalValue::ExternalLinkage, "caller", M);
+ BasicBlock *Entry = BasicBlock::Create(C, "entry", Caller);
+ CallInst *Indirect =
+ CallInst::Create(CalleeTy, Caller->getArg(0), {}, "", Entry);
+ CallInst *Direct = CallInst::Create(Target0, {}, "", Entry);
+ InlineAsm *Asm = InlineAsm::get(CalleeTy, "", "", true);
+ CallInst *InlineAsmCall = CallInst::Create(CalleeTy, Asm, {}, "", Entry);
+ ReturnInst::Create(C, Entry);
+
+ SmallVector<Function *, 4> Callees = {Target0};
+ EXPECT_FALSE(Indirect->getCalleesMetadata(Callees));
+ EXPECT_TRUE(Callees.empty());
+
+ Indirect->setMetadata(LLVMContext::MD_callees, MDNode::get(C, {}));
+ EXPECT_TRUE(Indirect->getCalleesMetadata(Callees));
+ EXPECT_TRUE(Callees.empty());
+
+ MDBuilder MDB(C);
+ Indirect->setMetadata(LLVMContext::MD_callees,
+ MDB.createCallees({Target0, Target1, Target0}));
+ ASSERT_TRUE(Indirect->getCalleesMetadata(Callees));
+ ASSERT_EQ(Callees.size(), 2u);
+ EXPECT_EQ(Callees[0], Target0);
+ EXPECT_EQ(Callees[1], Target1);
+
+ MDNode *Valid = MDB.createCallees({Target0});
+ Direct->setMetadata(LLVMContext::MD_callees, Valid);
+ Callees.push_back(Target1);
+ EXPECT_FALSE(Direct->getCalleesMetadata(Callees));
+ EXPECT_TRUE(Callees.empty());
+
+ InlineAsmCall->setMetadata(LLVMContext::MD_callees, Valid);
+ Callees.push_back(Target1);
+ EXPECT_FALSE(InlineAsmCall->getCalleesMetadata(Callees));
+ EXPECT_TRUE(Callees.empty());
+
+ Function *Invoker =
+ Function::Create(CallerTy, GlobalValue::ExternalLinkage, "invoker", M);
+ BasicBlock *InvokeEntry = BasicBlock::Create(C, "entry", Invoker);
+ BasicBlock *Normal = BasicBlock::Create(C, "normal", Invoker);
+ BasicBlock *Unwind = BasicBlock::Create(C, "unwind", Invoker);
+ InvokeInst *Invoke = InvokeInst::Create(CalleeTy, Invoker->getArg(0), Normal,
+ Unwind, {}, "", InvokeEntry);
+ ReturnInst::Create(C, Normal);
+ new UnreachableInst(C, Unwind);
+ Invoke->setMetadata(LLVMContext::MD_callees,
+ MDB.createCallees({Target1, Target0}));
+ ASSERT_TRUE(Invoke->getCalleesMetadata(Callees));
+ ASSERT_EQ(Callees.size(), 2u);
+ EXPECT_EQ(Callees[0], Target1);
+ EXPECT_EQ(Callees[1], Target0);
+}
+
+TEST(InstructionsTest, CalleesMetadataRejectsMalformedOperands) {
+ LLVMContext C;
+ Module M("test", C);
+ FunctionType *CalleeTy = FunctionType::get(Type::getVoidTy(C), false);
+ Function *Target =
+ Function::Create(CalleeTy, GlobalValue::ExternalLinkage, "target", M);
+ FunctionType *CallerTy =
+ FunctionType::get(Type::getVoidTy(C), PointerType::getUnqual(C), false);
+ Function *Caller =
+ Function::Create(CallerTy, GlobalValue::ExternalLinkage, "caller", M);
+ BasicBlock *Entry = BasicBlock::Create(C, "entry", Caller);
+ CallInst *Indirect =
+ CallInst::Create(CalleeTy, Caller->getArg(0), {}, "", Entry);
+ ReturnInst::Create(C, Entry);
+
+ auto *Global =
+ new GlobalVariable(M, Type::getInt8Ty(C), false,
+ GlobalValue::ExternalLinkage, nullptr, "global");
+ GlobalAlias *Alias = GlobalAlias::create(
+ Target->getType(), 0, GlobalValue::ExternalLinkage, "alias", Target, &M);
+ GlobalIFunc *IFunc = GlobalIFunc::create(
+ Target->getType(), 0, GlobalValue:...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/221550
More information about the llvm-commits
mailing list