[llvm] 921c6db - [llvm] Introduce callee_type metadata
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 18 14:40:58 PDT 2025
Author: Prabhu Rajasekaran
Date: 2025-07-18T14:40:54-07:00
New Revision: 921c6dbecaf49e3ed24b94802f094cd7f61f1873
URL: https://github.com/llvm/llvm-project/commit/921c6dbecaf49e3ed24b94802f094cd7f61f1873
DIFF: https://github.com/llvm/llvm-project/commit/921c6dbecaf49e3ed24b94802f094cd7f61f1873.diff
LOG: [llvm] Introduce callee_type metadata
Introduce `callee_type` metadata which will be attached to the indirect
call instructions.
The `callee_type` metadata will be used to generate `.callgraph` section
described in this RFC:
https://lists.llvm.org/pipermail/llvm-dev/2021-July/151739.html
Reviewers: morehouse, petrhosek, nikic, ilovepi
Reviewed By: nikic, ilovepi
Pull Request: https://github.com/llvm/llvm-project/pull/87573
Added:
llvm/docs/CalleeTypeMetadata.rst
llvm/test/Assembler/callee-type-metadata.ll
llvm/test/Transforms/Inline/drop-callee-type-metadata.ll
llvm/test/Transforms/InstCombine/drop-callee-type-metadata.ll
llvm/test/Transforms/SimplifyCFG/merge-callee-type-metadata.ll
llvm/test/Verifier/callee-type-metadata.ll
Modified:
llvm/docs/LangRef.rst
llvm/docs/Reference.rst
llvm/include/llvm/IR/FixedMetadataKinds.def
llvm/include/llvm/IR/Metadata.h
llvm/lib/IR/Metadata.cpp
llvm/lib/IR/Verifier.cpp
llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
llvm/lib/Transforms/Utils/Local.cpp
llvm/lib/Transforms/Utils/ValueMapper.cpp
Removed:
################################################################################
diff --git a/llvm/docs/CalleeTypeMetadata.rst b/llvm/docs/CalleeTypeMetadata.rst
new file mode 100644
index 0000000000000..45d0657966a8c
--- /dev/null
+++ b/llvm/docs/CalleeTypeMetadata.rst
@@ -0,0 +1,33 @@
+====================
+Callee Type Metadata
+====================
+
+Introduction
+============
+This ``!callee_type`` metadata is introduced to support the generation of a call graph
+section in the object file. The ``!callee_type`` metadata is used
+to identify the types of the intended callees of indirect call instructions. The ``!callee_type`` metadata is a
+list of one or more generalized ``!type`` metadata objects (See :doc:`TypeMetadata`) with each ``!type``
+metadata pointing to a callee's :ref:`type identifier <calleetype-type-identifier>`.
+LLVM's `Control Flow Integrity (CFI)`_ also uses the ``!type`` metadata in its implementation.
+
+.. _Control Flow Integrity (CFI): https://clang.llvm.org/docs/ControlFlowIntegrity.html
+
+.. _calleetype-type-identifier:
+
+Type identifier
+================
+
+The type for an indirect call target is the callee's function signature.
+Mapping from a type to an identifier is an ABI detail.
+In the current implementation, an identifier of type T is
+computed as follows:
+
+ - Obtain the generalized mangled name for “typeinfo name for T”.
+ - Compute MD5 hash of the name as a string.
+ - Reinterpret the first 8 bytes of the hash as a little-endian 64-bit integer.
+
+To avoid mismatched pointer types, generalizations are applied.
+Pointers in return and argument types are treated as equivalent as long as the qualifiers for the
+type they point to match. For example, ``char*``, ``char**``, and ``int*`` are considered equivalent
+types. However, ``char*`` and ``const char*`` are considered distinct types.
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 371f356c80b0a..be5f7fbd90b5e 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -8171,6 +8171,11 @@ change in the future.
See :doc:`TypeMetadata`.
+'``callee_type``' Metadata
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+See :doc:`CalleeTypeMetadata`.
+
'``associated``' Metadata
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/llvm/docs/Reference.rst b/llvm/docs/Reference.rst
index cb9576b15d701..35a6f59ecbf35 100644
--- a/llvm/docs/Reference.rst
+++ b/llvm/docs/Reference.rst
@@ -14,6 +14,7 @@ LLVM and API reference documentation.
BlockFrequencyTerminology
BranchWeightMetadata
Bugpoint
+ CalleeTypeMetadata
CIBestPractices
CommandGuide/index
ConvergenceAndUniformity
diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def
index df572e8791e13..90276eae13e4b 100644
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -53,3 +53,4 @@ LLVM_FIXED_MD_KIND(MD_DIAssignID, "DIAssignID", 38)
LLVM_FIXED_MD_KIND(MD_coro_outside_frame, "coro.outside.frame", 39)
LLVM_FIXED_MD_KIND(MD_mmra, "mmra", 40)
LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41)
+LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
diff --git a/llvm/include/llvm/IR/Metadata.h b/llvm/include/llvm/IR/Metadata.h
index 2de26c0c1f7c7..af252aa24567a 100644
--- a/llvm/include/llvm/IR/Metadata.h
+++ b/llvm/include/llvm/IR/Metadata.h
@@ -1255,6 +1255,13 @@ class MDNode : public Metadata {
bool isReplaceable() const { return isTemporary() || isAlwaysReplaceable(); }
bool isAlwaysReplaceable() const { return getMetadataID() == DIAssignIDKind; }
+ /// Check if this is a valid generalized type metadata node.
+ bool hasGeneralizedMDString() {
+ if (getNumOperands() < 2 || !isa<MDString>(getOperand(1)))
+ return false;
+ return cast<MDString>(getOperand(1))->getString().ends_with(".generalized");
+ }
+
unsigned getNumTemporaryUses() const {
assert(isTemporary() && "Only for temporaries");
return Context.getReplaceableUses()->getNumUses();
@@ -1467,6 +1474,8 @@ class MDNode : public Metadata {
const Instruction *BInstr);
LLVM_ABI static MDNode *getMergedMemProfMetadata(MDNode *A, MDNode *B);
LLVM_ABI static MDNode *getMergedCallsiteMetadata(MDNode *A, MDNode *B);
+ LLVM_ABI static MDNode *getMergedCalleeTypeMetadata(const MDNode *A,
+ const MDNode *B);
};
/// Tuple of metadata.
diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp
index f0448b06e7e82..0dbd07f4865dc 100644
--- a/llvm/lib/IR/Metadata.cpp
+++ b/llvm/lib/IR/Metadata.cpp
@@ -1303,6 +1303,24 @@ static void addRange(SmallVectorImpl<ConstantInt *> &EndPoints,
EndPoints.push_back(High);
}
+MDNode *MDNode::getMergedCalleeTypeMetadata(const MDNode *A, const MDNode *B) {
+ // Drop the callee_type metadata if either of the call instructions do not
+ // have it.
+ if (!A || !B)
+ return nullptr;
+ SmallVector<Metadata *, 8> AB;
+ SmallPtrSet<Metadata *, 8> MergedCallees;
+ auto AddUniqueCallees = [&AB, &MergedCallees](const MDNode *N) {
+ for (Metadata *MD : N->operands()) {
+ if (MergedCallees.insert(MD).second)
+ AB.push_back(MD);
+ }
+ };
+ AddUniqueCallees(A);
+ AddUniqueCallees(B);
+ return MDNode::get(A->getContext(), AB);
+}
+
MDNode *MDNode::getMostGenericRange(MDNode *A, MDNode *B) {
// Given two ranges, we want to compute the union of the ranges. This
// is slightly complicated by having to combine the intervals and merge
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 8c8ed3c5e47ba..9bd573e773610 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -531,6 +531,7 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
void visitCallStackMetadata(MDNode *MD);
void visitMemProfMetadata(Instruction &I, MDNode *MD);
void visitCallsiteMetadata(Instruction &I, MDNode *MD);
+ void visitCalleeTypeMetadata(Instruction &I, MDNode *MD);
void visitDIAssignIDMetadata(Instruction &I, MDNode *MD);
void visitMMRAMetadata(Instruction &I, MDNode *MD);
void visitAnnotationMetadata(MDNode *Annotation);
@@ -5193,6 +5194,33 @@ void Verifier::visitCallsiteMetadata(Instruction &I, MDNode *MD) {
visitCallStackMetadata(MD);
}
+static inline bool isConstantIntMetadataOperand(const Metadata *MD) {
+ if (auto *VAL = dyn_cast<ValueAsMetadata>(MD))
+ return isa<ConstantInt>(VAL->getValue());
+ return false;
+}
+
+void Verifier::visitCalleeTypeMetadata(Instruction &I, MDNode *MD) {
+ Check(isa<CallBase>(I), "!callee_type metadata should only exist on calls",
+ &I);
+ for (Metadata *Op : MD->operands()) {
+ Check(isa<MDNode>(Op),
+ "The callee_type metadata must be a list of type metadata nodes", Op);
+ auto *TypeMD = cast<MDNode>(Op);
+ Check(TypeMD->getNumOperands() == 2,
+ "Well-formed generalized type metadata must contain exactly two "
+ "operands",
+ Op);
+ Check(isConstantIntMetadataOperand(TypeMD->getOperand(0)) &&
+ mdconst::extract<ConstantInt>(TypeMD->getOperand(0))->isZero(),
+ "The first operand of type metadata for functions must be zero", Op);
+ Check(TypeMD->hasGeneralizedMDString(),
+ "Only generalized type metadata can be part of the callee_type "
+ "metadata list",
+ Op);
+ }
+}
+
void Verifier::visitAnnotationMetadata(MDNode *Annotation) {
Check(isa<MDTuple>(Annotation), "annotation must be a tuple");
Check(Annotation->getNumOperands() >= 1,
@@ -5470,6 +5498,9 @@ void Verifier::visitInstruction(Instruction &I) {
if (MDNode *MD = I.getMetadata(LLVMContext::MD_callsite))
visitCallsiteMetadata(I, MD);
+ if (MDNode *MD = I.getMetadata(LLVMContext::MD_callee_type))
+ visitCalleeTypeMetadata(I, MD);
+
if (MDNode *MD = I.getMetadata(LLVMContext::MD_DIAssignID))
visitDIAssignIDMetadata(I, MD);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 3321435a6fecb..d88bc2c4901c7 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -4352,6 +4352,13 @@ Instruction *InstCombinerImpl::visitCallBase(CallBase &Call) {
Call, Builder.CreateBitOrPointerCast(ReturnedArg, CallTy));
}
+ // Drop unnecessary callee_type metadata from calls that were converted
+ // into direct calls.
+ if (Call.getMetadata(LLVMContext::MD_callee_type) && !Call.isIndirectCall()) {
+ Call.setMetadata(LLVMContext::MD_callee_type, nullptr);
+ Changed = true;
+ }
+
// Drop unnecessary kcfi operand bundles from calls that were converted
// into direct calls.
auto Bundle = Call.getOperandBundle(LLVMContext::OB_kcfi);
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index d481ad9dee181..7f0c23bd24efb 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -3005,6 +3005,12 @@ static void combineMetadata(Instruction *K, const Instruction *J,
case LLVMContext::MD_memprof:
case LLVMContext::MD_callsite:
break;
+ case LLVMContext::MD_callee_type:
+ if (!AAOnly) {
+ K->setMetadata(LLVMContext::MD_callee_type,
+ MDNode::getMergedCalleeTypeMetadata(KMD, JMD));
+ }
+ break;
case LLVMContext::MD_align:
if (!AAOnly && (DoesKMove || !K->hasMetadata(LLVMContext::MD_noundef)))
K->setMetadata(
diff --git a/llvm/lib/Transforms/Utils/ValueMapper.cpp b/llvm/lib/Transforms/Utils/ValueMapper.cpp
index 7ba95e299c1b1..8d8a60b6918fe 100644
--- a/llvm/lib/Transforms/Utils/ValueMapper.cpp
+++ b/llvm/lib/Transforms/Utils/ValueMapper.cpp
@@ -987,6 +987,13 @@ void Mapper::remapInstruction(Instruction *I) {
"Referenced value not in value map!");
}
+ // Drop callee_type metadata from calls that were remapped
+ // into a direct call from an indirect one.
+ if (auto *CB = dyn_cast<CallBase>(I)) {
+ if (CB->getMetadata(LLVMContext::MD_callee_type) && !CB->isIndirectCall())
+ CB->setMetadata(LLVMContext::MD_callee_type, nullptr);
+ }
+
// Remap phi nodes' incoming blocks.
if (PHINode *PN = dyn_cast<PHINode>(I)) {
for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) {
diff --git a/llvm/test/Assembler/callee-type-metadata.ll b/llvm/test/Assembler/callee-type-metadata.ll
new file mode 100644
index 0000000000000..9c3cfbe82fc13
--- /dev/null
+++ b/llvm/test/Assembler/callee-type-metadata.ll
@@ -0,0 +1,21 @@
+;; Test if the callee_type metadata attached to indirect call sites adhere to the expected format.
+
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+define i32 @_Z13call_indirectPFicEc(ptr %func, i8 signext %x) !type !0 {
+entry:
+ %func.addr = alloca ptr, align 8
+ %x.addr = alloca i8, align 1
+ store ptr %func, ptr %func.addr, align 8
+ store i8 %x, ptr %x.addr, align 1
+ %fptr = load ptr, ptr %func.addr, align 8
+ %x_val = load i8, ptr %x.addr, align 1
+ ; CHECK: %call = call i32 %fptr(i8 signext %x_val), !callee_type !1
+ %call = call i32 %fptr(i8 signext %x_val), !callee_type !1
+ ret i32 %call
+}
+
+declare !type !2 i32 @_Z3barc(i8 signext)
+
+!0 = !{i64 0, !"_ZTSFiPvcE.generalized"}
+!1 = !{!2}
+!2 = !{i64 0, !"_ZTSFicE.generalized"}
diff --git a/llvm/test/Transforms/Inline/drop-callee-type-metadata.ll b/llvm/test/Transforms/Inline/drop-callee-type-metadata.ll
new file mode 100644
index 0000000000000..547588089c5b0
--- /dev/null
+++ b/llvm/test/Transforms/Inline/drop-callee-type-metadata.ll
@@ -0,0 +1,41 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+;; Test if the callee_type metadata is dropped when it is
+;; is mapped to a direct function call from an indirect call during inlining.
+
+; RUN: opt -passes=inline -S < %s | FileCheck %s
+
+define i32 @_Z13call_indirectPFicEc(ptr %func, i8 %x) !type !0 {
+; CHECK-LABEL: define i32 @_Z13call_indirectPFicEc(
+; CHECK-SAME: ptr [[FUNC:%.*]], i8 [[X:%.*]]) !type [[META0:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CALL:%.*]] = call i32 [[FUNC]](i8 [[X]]), !callee_type [[META1:![0-9]+]]
+; CHECK-NEXT: ret i32 [[CALL]]
+;
+entry:
+ %call = call i32 %func(i8 %x), !callee_type !1
+ ret i32 %call
+}
+
+define i32 @_Z3barv() !type !3 {
+; CHECK-LABEL: define i32 @_Z3barv(
+; CHECK-SAME: ) !type [[META3:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CALL_I:%.*]] = call i32 @_Z3fooc(i8 97)
+; CHECK-NEXT: ret i32 [[CALL_I]]
+;
+entry:
+ %call = call i32 @_Z13call_indirectPFicEc(ptr nonnull @_Z3fooc, i8 97)
+ ret i32 %call
+}
+declare !type !2 i32 @_Z3fooc(i8 signext)
+
+!0 = !{i64 0, !"_ZTSFiPvcE.generalized"}
+!1 = !{!2}
+!2 = !{i64 0, !"_ZTSFicE.generalized"}
+!3 = !{i64 0, !"_ZTSFivE.generalized"}
+;.
+; CHECK: [[META0]] = !{i64 0, !"_ZTSFiPvcE.generalized"}
+; CHECK: [[META1]] = !{[[META2:![0-9]+]]}
+; CHECK: [[META2]] = !{i64 0, !"_ZTSFicE.generalized"}
+; CHECK: [[META3]] = !{i64 0, !"_ZTSFivE.generalized"}
+;.
diff --git a/llvm/test/Transforms/InstCombine/drop-callee-type-metadata.ll b/llvm/test/Transforms/InstCombine/drop-callee-type-metadata.ll
new file mode 100644
index 0000000000000..83215f78be1b0
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/drop-callee-type-metadata.ll
@@ -0,0 +1,25 @@
+;; Test if the callee_type metadata is dropped when it is attached
+;; to a direct function call during instcombine.
+
+; RUN: opt -passes=instcombine -S < %s | FileCheck %s
+
+define i32 @_Z3barv() !type !0 {
+; CHECK-LABEL: define i32 @_Z3barv(
+; CHECK-SAME: ) !type [[META0:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CALL:%.*]] = call i32 @_Z3fooc(i8 97){{$}}
+; CHECK-NEXT: ret i32 [[CALL]]
+;
+entry:
+ %call = call i32 @_Z3fooc(i8 97), !callee_type !1
+ ret i32 %call
+}
+
+declare !type !2 i32 @_Z3fooc(i8 signext)
+
+!0 = !{i64 0, !"_ZTSFivE.generalized"}
+!1 = !{!2}
+!2 = !{i64 0, !"_ZTSFicE.generalized"}
+;.
+; CHECK: [[META0]] = !{i64 0, !"_ZTSFivE.generalized"}
+;.
diff --git a/llvm/test/Transforms/SimplifyCFG/merge-callee-type-metadata.ll b/llvm/test/Transforms/SimplifyCFG/merge-callee-type-metadata.ll
new file mode 100644
index 0000000000000..3e56939b1642f
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/merge-callee-type-metadata.ll
@@ -0,0 +1,167 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 5
+;; Test if the callee_type metadata is merged correctly.
+
+; RUN: opt -passes=simplifycfg -S < %s | FileCheck %s
+
+;; Test if the callee_type metadata is merged correctly when
+;; the instructions carry
diff erring callee_type metadata.
+define ptr @_Z10test_
diff b(i1 zeroext %b) {
+; CHECK-LABEL: define ptr @_Z10test_
diff b(
+; CHECK-SAME: i1 zeroext [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN:%.*]] = alloca ptr, align 8
+; CHECK-NEXT: store ptr @_Znwm, ptr [[FN]], align 8
+; CHECK-NEXT: [[CALL:%.*]] = call ptr [[FN]](i64 4), !callee_type [[META0:![0-9]+]]
+; CHECK-NEXT: ret ptr [[CALL]]
+;
+entry:
+ %fn = alloca ptr
+ store ptr @_Znwm, ptr %fn
+ br i1 %b, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %call = call ptr %fn(i64 4), !callee_type !4
+ br label %if.end
+
+if.else: ; preds = %entry
+ %call1 = call ptr %fn(i64 4), !callee_type !3
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ]
+ ret ptr %x.0
+}
+
+;; Test if the callee_type metadata is merged correctly when
+;; the instructions carry same callee_type metadata.
+define ptr @_Z10test_sameb(i1 zeroext %b) {
+; CHECK-LABEL: define ptr @_Z10test_sameb(
+; CHECK-SAME: i1 zeroext [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN:%.*]] = alloca ptr, align 8
+; CHECK-NEXT: store ptr @_Znwm, ptr [[FN]], align 8
+; CHECK-NEXT: [[CALL:%.*]] = call ptr [[FN]](i64 4), !callee_type [[META3:![0-9]+]]
+; CHECK-NEXT: ret ptr [[CALL]]
+;
+entry:
+ %fn = alloca ptr
+ store ptr @_Znwm, ptr %fn
+ br i1 %b, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %call = call ptr %fn(i64 4), !callee_type !3
+ br label %if.end
+
+if.else: ; preds = %entry
+ %call1 = call ptr %fn(i64 4), !callee_type !3
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ]
+ ret ptr %x.0
+}
+
+;; Test if the callee_type metadata is dropped correctly when
+;; only the left instruction has callee_type metadata.
+define ptr @_Z10test_leftb(i1 zeroext %b) {
+; CHECK-LABEL: define ptr @_Z10test_leftb(
+; CHECK-SAME: i1 zeroext [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN:%.*]] = alloca ptr, align 8
+; CHECK-NEXT: store ptr @_Znwm, ptr [[FN]], align 8
+; CHECK-NEXT: [[CALL:%.*]] = call ptr [[FN]](i64 4)
+; CHECK-NEXT: ret ptr [[CALL]]
+;
+entry:
+ %fn = alloca ptr
+ store ptr @_Znwm, ptr %fn
+ br i1 %b, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %call = call ptr %fn(i64 4), !callee_type !4
+ br label %if.end
+
+if.else: ; preds = %entry
+ %call1 = call ptr %fn(i64 4)
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ]
+ ret ptr %x.0
+}
+
+;; Test if the callee_type metadata is dropped correctly when
+;; only the right instruction has callee_type metadata.
+define ptr @_Z10test_rightb(i1 zeroext %b) {
+; CHECK-LABEL: define ptr @_Z10test_rightb(
+; CHECK-SAME: i1 zeroext [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN:%.*]] = alloca ptr, align 8
+; CHECK-NEXT: store ptr @_Znwm, ptr [[FN]], align 8
+; CHECK-NEXT: [[CALL:%.*]] = call ptr [[FN]](i64 4)
+; CHECK-NEXT: ret ptr [[CALL]]
+;
+entry:
+ %fn = alloca ptr
+ store ptr @_Znwm, ptr %fn
+ br i1 %b, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %call = call ptr %fn(i64 4)
+ br label %if.end
+
+if.else: ; preds = %entry
+ %call1 = call ptr %fn(i64 4), !callee_type !3
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ]
+ ret ptr %x.0
+}
+
+;; Test if the callee_type metadata is merged correctly when
+;; each of the callee_type metadata are lists.
+define ptr @_Z10test_listb(i1 zeroext %b) {
+; CHECK-LABEL: define ptr @_Z10test_listb(
+; CHECK-SAME: i1 zeroext [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[FN:%.*]] = alloca ptr, align 8
+; CHECK-NEXT: store ptr @_Znwm, ptr [[FN]], align 8
+; CHECK-NEXT: [[CALL:%.*]] = call ptr [[FN]](i64 4), !callee_type [[META4:![0-9]+]]
+; CHECK-NEXT: ret ptr [[CALL]]
+;
+entry:
+ %fn = alloca ptr
+ store ptr @_Znwm, ptr %fn
+ br i1 %b, label %if.then, label %if.else
+
+if.then: ; preds = %entry
+ %call = call ptr %fn(i64 4), !callee_type !6
+ br label %if.end
+
+if.else: ; preds = %entry
+ %call1 = call ptr %fn(i64 4), !callee_type !5
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ]
+ ret ptr %x.0
+}
+
+declare ptr @_Znwm(i64)
+
+!0 = !{i64 0, !"callee_type0.generalized"}
+!1 = !{i64 0, !"callee_type1.generalized"}
+!2 = !{i64 0, !"callee_type2.generalized"}
+!3 = !{!0}
+!4 = !{!2}
+!5 = !{!1, !2}
+!6 = !{!0, !2}
+;.
+; CHECK: [[META0]] = !{[[META1:![0-9]+]], [[META2:![0-9]+]]}
+; CHECK: [[META1]] = !{i64 0, !"callee_type2.generalized"}
+; CHECK: [[META2]] = !{i64 0, !"callee_type0.generalized"}
+; CHECK: [[META3]] = !{[[META2]]}
+; CHECK: [[META4]] = !{[[META2]], [[META1]], [[META5:![0-9]+]]}
+; CHECK: [[META5]] = !{i64 0, !"callee_type1.generalized"}
+;.
diff --git a/llvm/test/Verifier/callee-type-metadata.ll b/llvm/test/Verifier/callee-type-metadata.ll
new file mode 100644
index 0000000000000..50cf37b941fe9
--- /dev/null
+++ b/llvm/test/Verifier/callee-type-metadata.ll
@@ -0,0 +1,33 @@
+;; Test if the callee_type metadata attached to indirect call sites adhere to the expected format.
+
+; RUN: not llvm-as -disable-output < %s 2>&1 | FileCheck %s
+define i32 @_Z13call_indirectPFicEc(ptr %func, i8 signext %x) !type !0 {
+entry:
+ %func.addr = alloca ptr, align 8
+ %x.addr = alloca i8, align 1
+ store ptr %func, ptr %func.addr, align 8
+ store i8 %x, ptr %x.addr, align 1
+ %fptr = load ptr, ptr %func.addr, align 8
+ %x_val = load i8, ptr %x.addr, align 1
+ ; CHECK: The callee_type metadata must be a list of type metadata nodes
+ %call = call i32 %fptr(i8 signext %x_val), !callee_type !0
+ ; CHECK: Well-formed generalized type metadata must contain exactly two operands
+ %call1 = call i32 %fptr(i8 signext %x_val), !callee_type !2
+ ; CHECK: The first operand of type metadata for functions must be zero
+ %call2 = call i32 %fptr(i8 signext %x_val), !callee_type !4
+ ; CHECK: The first operand of type metadata for functions must be zero
+ %call3 = call i32 %fptr(i8 signext %x_val), !callee_type !6
+ ; CHECK: Only generalized type metadata can be part of the callee_type metadata list
+ %call4 = call i32 %fptr(i8 signext %x_val), !callee_type !8
+ ret i32 %call
+}
+
+!0 = !{i64 0, !"_ZTSFiPvcE.generalized"}
+!1 = !{!"_ZTSFicE"}
+!2 = !{!2}
+!3 = !{i64 1, !"_ZTSFicE"}
+!4 = !{!3}
+!5 = !{!"expected_int", !"_ZTSFicE"}
+!6 = !{!5}
+!7 = !{i64 0, !"_ZTSFicE"}
+!8 = !{!7}
More information about the llvm-commits
mailing list