[clang] 550b62c - [clang] callee_type metadata for indirect calls (#163233)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 21 08:02:58 PDT 2025
Author: Prabhu Rajasekaran
Date: 2025-10-21T08:02:52-07:00
New Revision: 550b62cbcbcb1ec8ec16c2dc0812bc23383962d6
URL: https://github.com/llvm/llvm-project/commit/550b62cbcbcb1ec8ec16c2dc0812bc23383962d6
DIFF: https://github.com/llvm/llvm-project/commit/550b62cbcbcb1ec8ec16c2dc0812bc23383962d6.diff
LOG: [clang] callee_type metadata for indirect calls (#163233)
Create and add generalized type identifier metadata to indirect calls,
and to functions which are potential indirect call targets.
The functions carry the !type metadata. The indirect callsites carry a
list of !type metadata values under !callee_type metadata.
RFC:
https://discourse.llvm.org/t/rfc-call-graph-information-from-clang-llvm-for-c-c/88255
Added:
clang/test/CodeGen/call-graph-section-callback.cpp
clang/test/CodeGen/call-graph-section-templates.cpp
clang/test/CodeGen/call-graph-section-virtual-methods.cpp
clang/test/CodeGen/call-graph-section.c
clang/test/CodeGen/call-graph-section.cpp
Modified:
clang/lib/CodeGen/CGCall.cpp
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/CodeGen/CodeGenModule.h
Removed:
################################################################################
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 741fa44713ac8..465f3f4e670c2 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -5937,8 +5937,24 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
CI->getCalledFunction()->getName().starts_with("_Z4sqrt")) {
SetSqrtFPAccuracy(CI);
}
- if (callOrInvoke)
+ if (callOrInvoke) {
*callOrInvoke = CI;
+ if (CGM.getCodeGenOpts().CallGraphSection) {
+ QualType CST;
+ if (TargetDecl && TargetDecl->getFunctionType())
+ CST = QualType(TargetDecl->getFunctionType(), 0);
+ else if (const auto *FPT =
+ Callee.getAbstractInfo().getCalleeFunctionProtoType())
+ CST = QualType(FPT, 0);
+ else
+ llvm_unreachable(
+ "Cannot find the callee type to generate callee_type metadata.");
+
+ // Set type identifier metadata of indirect calls for call graph section.
+ if (!CST.isNull())
+ CGM.createCalleeTypeMetadataForIcall(CST, *callOrInvoke);
+ }
+ }
// If this is within a function that has the guard(nocf) attribute and is an
// indirect call, add the "guard_nocf" attribute to this call to indicate that
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 1085f45e0fc21..e490b1ce0efa4 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2863,6 +2863,11 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
}
}
+ if (CodeGenOpts.CallGraphSection) {
+ if (auto *FD = dyn_cast<FunctionDecl>(D))
+ createIndirectFunctionTypeMD(FD, F);
+ }
+
// Emit type metadata on member functions for member function pointer checks.
// These are only ever necessary on definitions; we're guaranteed that the
// definition will be present in the LTO unit as a result of LTO visibility.
@@ -3066,6 +3071,26 @@ static void setLinkageForGV(llvm::GlobalValue *GV, const NamedDecl *ND) {
GV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
}
+static bool hasExistingGeneralizedTypeMD(llvm::Function *F) {
+ llvm::MDNode *MD = F->getMetadata(llvm::LLVMContext::MD_type);
+ return MD && MD->hasGeneralizedMDString();
+}
+
+void CodeGenModule::createIndirectFunctionTypeMD(const FunctionDecl *FD,
+ llvm::Function *F) {
+ // Return if generalized type metadata is already attached.
+ if (hasExistingGeneralizedTypeMD(F))
+ return;
+
+ // All functions which are not internal linkage could be indirect targets.
+ // Address taken functions with internal linkage could be indirect targets.
+ if (!F->hasLocalLinkage() ||
+ F->getFunction().hasAddressTaken(nullptr, /*IgnoreCallbackUses=*/true,
+ /*IgnoreAssumeLikeCalls=*/true,
+ /*IgnoreLLVMUsed=*/false))
+ F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType()));
+}
+
void CodeGenModule::createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
llvm::Function *F) {
// Only if we are checking indirect calls.
@@ -3081,10 +3106,12 @@ void CodeGenModule::createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
/*GeneralizePointers=*/false);
llvm::Metadata *MD = CreateMetadataIdentifierForType(FnType);
F->addTypeMetadata(0, MD);
-
- QualType GenPtrFnType = GeneralizeFunctionType(getContext(), FD->getType(),
- /*GeneralizePointers=*/true);
- F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(GenPtrFnType));
+ // Add the generalized identifier if not added already.
+ if (!hasExistingGeneralizedTypeMD(F)) {
+ QualType GenPtrFnType = GeneralizeFunctionType(getContext(), FD->getType(),
+ /*GeneralizePointers=*/true);
+ F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(GenPtrFnType));
+ }
// Emit a hash-based bit set entry for cross-DSO calls.
if (CodeGenOpts.SanitizeCfiCrossDso)
@@ -3092,6 +3119,21 @@ void CodeGenModule::createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
F->addTypeMetadata(0, llvm::ConstantAsMetadata::get(CrossDsoTypeId));
}
+void CodeGenModule::createCalleeTypeMetadataForIcall(const QualType &QT,
+ llvm::CallBase *CB) {
+ // Only if needed for call graph section and only for indirect calls.
+ if (!CodeGenOpts.CallGraphSection || !CB->isIndirectCall())
+ return;
+
+ llvm::Metadata *TypeIdMD = CreateMetadataIdentifierGeneralized(QT);
+ llvm::MDTuple *TypeTuple = llvm::MDTuple::get(
+ getLLVMContext(), {llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
+ llvm::Type::getInt64Ty(getLLVMContext()), 0)),
+ TypeIdMD});
+ llvm::MDTuple *MDN = llvm::MDNode::get(getLLVMContext(), {TypeTuple});
+ CB->setMetadata(llvm::LLVMContext::MD_callee_type, MDN);
+}
+
void CodeGenModule::setKCFIType(const FunctionDecl *FD, llvm::Function *F) {
llvm::LLVMContext &Ctx = F->getContext();
llvm::MDBuilder MDB(Ctx);
@@ -3227,6 +3269,9 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F,
!CodeGenOpts.SanitizeCfiCanonicalJumpTables)
createFunctionTypeMetadataForIcall(FD, F);
+ if (CodeGenOpts.CallGraphSection)
+ createIndirectFunctionTypeMD(FD, F);
+
if (LangOpts.Sanitize.has(SanitizerKind::KCFI))
setKCFIType(FD, F);
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index 3971b296b3f80..a253bcda2d06c 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1644,6 +1644,13 @@ class CodeGenModule : public CodeGenTypeCache {
void createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
llvm::Function *F);
+ /// Create and attach type metadata if the function is a potential indirect
+ /// call target to support call graph section.
+ void createIndirectFunctionTypeMD(const FunctionDecl *FD, llvm::Function *F);
+
+ /// Create and attach type metadata to the given call.
+ void createCalleeTypeMetadataForIcall(const QualType &QT, llvm::CallBase *CB);
+
/// Set type metadata to the given function.
void setKCFIType(const FunctionDecl *FD, llvm::Function *F);
diff --git a/clang/test/CodeGen/call-graph-section-callback.cpp b/clang/test/CodeGen/call-graph-section-callback.cpp
new file mode 100644
index 0000000000000..e9b0a1818e3a4
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section-callback.cpp
@@ -0,0 +1,30 @@
+// Tests that callback function whose address is taken is attached Type ID metadata
+// as it is a potential indirect call target.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
+// RUN: -emit-llvm -o %t %s
+// RUN: FileCheck %s < %t
+
+////////////////////////////////////////////////////////////////////////////////
+typedef void (*CallbackFn)(int);
+
+// Callback function with "internal" linkage.
+// CHECK-LABEL: define internal void @_ZL10myCallbacki(
+// CHECK-SAME: {{.*}} !type [[F_CALLBACK:![0-9]+]]
+static void myCallback(int value)
+{
+ volatile int sink = value;
+ (void)sink;
+}
+
+int takeCallbackAddress() {
+ // Take the address of the callback explicitly (address-taken function)
+ CallbackFn cb = &myCallback;
+ // Store the address in a volatile pointer to keep it observable
+ volatile void* addr = (void*)cb;
+ (void)addr;
+
+ return 0;
+}
+
+// CHECK: [[F_CALLBACK]] = !{i64 0, !"_ZTSFviE.generalized"}
diff --git a/clang/test/CodeGen/call-graph-section-templates.cpp b/clang/test/CodeGen/call-graph-section-templates.cpp
new file mode 100644
index 0000000000000..39030d27a4ea9
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section-templates.cpp
@@ -0,0 +1,117 @@
+// Tests that we assign appropriate identifiers to indirect calls and targets
+// specifically for C++ templates.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
+// RUN: -emit-llvm -o %t %s
+// RUN: FileCheck --check-prefix=FT %s < %t
+// RUN: FileCheck --check-prefix=CST %s < %t
+
+////////////////////////////////////////////////////////////////////////////////
+// Class definitions and template classes (check for indirect target metadata)
+
+class Cls1 {};
+
+// Cls2 is instantiated with T=Cls1 in foo(). Following checks are for this
+// instantiation.
+template <class T>
+class Cls2 {
+public:
+ // FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f1Ev(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F1:![0-9]+]]
+ void f1() {}
+
+ // FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f2ES0_(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F2:![0-9]+]]
+ void f2(T a) {}
+
+ // FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f3EPS0_(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F3:![0-9]+]]
+ void f3(T *a) {}
+
+ // FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f4EPKS0_(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F4:![0-9]+]]
+ void f4(const T *a) {}
+
+ // FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f5ERS0_(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F5:![0-9]+]]
+ void f5(T &a) {}
+
+ // FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f6ERKS0_(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F6:![0-9]+]]
+ void f6(const T &a) {}
+
+ // Mixed type function pointer member
+ T *(*fp)(T a, T *b, const T *c, T &d, const T &e);
+};
+
+// FT: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFvvE.generalized"}
+// FT: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"}
+// FT: [[F_TCLS2F3]] = !{i64 0, !"_ZTSFvP4Cls1E.generalized"}
+// FT: [[F_TCLS2F4]] = !{i64 0, !"_ZTSFvPK4Cls1E.generalized"}
+// FT: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"}
+// FT: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"}
+
+////////////////////////////////////////////////////////////////////////////////
+// Callsites (check for indirect callsite operand bundles)
+
+template <class T>
+T *T_func(T a, T *b, const T *c, T &d, const T &e) { return b; }
+
+// CST-LABEL: define {{.*}} @_Z3foov
+// CST-SAME: {{.*}} !type [[F_TCLS2F1:![0-9]+]]
+void foo() {
+ // Methods for Cls2<Cls1> is checked above within the template description.
+ Cls2<Cls1> Obj;
+
+ Obj.fp = T_func<Cls1>;
+ Cls1 Cls1Obj;
+
+ // CST: call noundef ptr %{{.*}}, !callee_type [[F_TFUNC_CLS1_CT:![0-9]+]]
+ Obj.fp(Cls1Obj, &Cls1Obj, &Cls1Obj, Cls1Obj, Cls1Obj);
+
+ // Make indirect calls to Cls2's member methods
+ auto fp_f1 = &Cls2<Cls1>::f1;
+ auto fp_f2 = &Cls2<Cls1>::f2;
+ auto fp_f3 = &Cls2<Cls1>::f3;
+ auto fp_f4 = &Cls2<Cls1>::f4;
+ auto fp_f5 = &Cls2<Cls1>::f5;
+ auto fp_f6 = &Cls2<Cls1>::f6;
+
+ auto *Obj2Ptr = &Obj;
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F1_CT:![0-9]+]]
+ (Obj2Ptr->*fp_f1)();
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F2_CT:![0-9]+]]
+ (Obj2Ptr->*fp_f2)(Cls1Obj);
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F3_CT:![0-9]+]]
+ (Obj2Ptr->*fp_f3)(&Cls1Obj);
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F4_CT:![0-9]+]]
+ (Obj2Ptr->*fp_f4)(&Cls1Obj);
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F5_CT:![0-9]+]]
+ (Obj2Ptr->*fp_f5)(Cls1Obj);
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F6_CT:![0-9]+]]
+ (Obj2Ptr->*fp_f6)(Cls1Obj);
+}
+
+// CST-LABEL: define {{.*}} @_Z6T_funcI4Cls1EPT_S1_S2_PKS1_RS1_RS3_(
+// CST-SAME: {{.*}} !type [[F_TFUNC_CLS1:![0-9]+]]
+
+// CST: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFvvE.generalized"}
+// CST: [[F_TFUNC_CLS1_CT]] = !{[[F_TFUNC_CLS1:![0-9]+]]}
+// CST: [[F_TFUNC_CLS1]] = !{i64 0, !"_ZTSFP4Cls1S_S0_PKS_RS_RS1_E.generalized"}
+// CST: [[F_TCLS2F1_CT]] = !{[[F_TCLS2F1:![0-9]+]]}
+// CST: [[F_TCLS2F2_CT]] = !{[[F_TCLS2F2:![0-9]+]]}
+// CST: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"}
+// CST: [[F_TCLS2F3_CT]] = !{[[F_TCLS2F3:![0-9]+]]}
+// CST: [[F_TCLS2F3]] = !{i64 0, !"_ZTSFvP4Cls1E.generalized"}
+// CST: [[F_TCLS2F4_CT]] = !{[[F_TCLS2F4:![0-9]+]]}
+// CST: [[F_TCLS2F4]] = !{i64 0, !"_ZTSFvPK4Cls1E.generalized"}
+// CST: [[F_TCLS2F5_CT]] = !{[[F_TCLS2F5:![0-9]+]]}
+// CST: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"}
+// CST: [[F_TCLS2F6_CT]] = !{[[F_TCLS2F6:![0-9]+]]}
+// CST: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"}
diff --git a/clang/test/CodeGen/call-graph-section-virtual-methods.cpp b/clang/test/CodeGen/call-graph-section-virtual-methods.cpp
new file mode 100644
index 0000000000000..afeeae146ec41
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section-virtual-methods.cpp
@@ -0,0 +1,56 @@
+// Tests that we assign appropriate identifiers to indirect calls and targets
+// specifically for virtual methods.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
+// RUN: -emit-llvm -o %t %s
+// RUN: FileCheck --check-prefix=FT %s < %t
+// RUN: FileCheck --check-prefix=CST %s < %t
+
+////////////////////////////////////////////////////////////////////////////////
+// Class definitions (check for indirect target metadata)
+
+class Base {
+ public:
+ // FT-LABEL: define {{.*}} @_ZN4Base2vfEPc(
+ // FT-SAME: {{.*}} !type [[F_TVF:![0-9]+]]
+ virtual int vf(char *a) { return 0; };
+ };
+
+ class Derived : public Base {
+ public:
+ // FT: define {{.*}} @_ZN7Derived2vfEPc({{.*}} !type [[F_TVF]]
+ int vf(char *a) override { return 1; };
+ };
+
+ // FT: [[F_TVF]] = !{i64 0, !"_ZTSFiPcE.generalized"}
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Callsites (check for indirect callsite operand bundles)
+
+ // CST-LABEL: define {{.*}} @_Z3foov
+ void foo() {
+ auto B = Base();
+ auto D = Derived();
+
+ Base *Bptr = &B;
+ Base *BptrToD = &D;
+ Derived *Dptr = &D;
+
+ auto FpBaseVf = &Base::vf;
+ auto FpDerivedVf = &Derived::vf;
+
+ // CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
+ (Bptr->*FpBaseVf)(0);
+
+ // CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
+ (BptrToD->*FpBaseVf)(0);
+
+ // CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
+ (Dptr->*FpBaseVf)(0);
+
+ // CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
+ (Dptr->*FpDerivedVf)(0);
+ }
+
+ // CST: [[F_TVF_CT]] = !{[[F_TVF:![0-9]+]]}
+ // CST: [[F_TVF]] = !{i64 0, !"_ZTSFiPcE.generalized"}
diff --git a/clang/test/CodeGen/call-graph-section.c b/clang/test/CodeGen/call-graph-section.c
new file mode 100644
index 0000000000000..69cdd59549190
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section.c
@@ -0,0 +1,93 @@
+// Tests that we assign appropriate identifiers to indirect calls and targets.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
+// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,ITANIUM %s
+
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fexperimental-call-graph-section \
+// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,MS %s
+
+// CHECK-LABEL: define {{(dso_local)?}} void @foo(
+// CHECK-SAME: {{.*}} !type [[F_TVOID:![0-9]+]]
+void foo() {
+}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @bar(
+// CHECK-SAME: {{.*}} !type [[F_TVOID]]
+void bar() {
+ void (*fp)() = foo;
+ // ITANIUM: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
+ // MS: call {{.*}}, !callee_type [[F_TVOID_CT:![0-9]+]]
+ fp();
+}
+
+// CHECK-LABEL: define {{(dso_local)?}} i32 @baz(
+// CHECK-SAME: {{.*}} !type [[F_TPRIMITIVE:![0-9]+]]
+int baz(char a, float b, double c) {
+ return 1;
+}
+
+// CHECK-LABEL: define {{(dso_local)?}} ptr @qux(
+// CHECK-SAME: {{.*}} !type [[F_TPTR:![0-9]+]]
+int *qux(char *a, float *b, double *c) {
+ return 0;
+}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @corge(
+// CHECK-SAME: {{.*}} !type [[F_TVOID]]
+void corge() {
+ int (*fp_baz)(char, float, double) = baz;
+ // CHECK: call i32 {{.*}}, !callee_type [[F_TPRIMITIVE_CT:![0-9]+]]
+ fp_baz('a', .0f, .0);
+
+ int *(*fp_qux)(char *, float *, double *) = qux;
+ // CHECK: call ptr {{.*}}, !callee_type [[F_TPTR_CT:![0-9]+]]
+ fp_qux(0, 0, 0);
+}
+
+struct st1 {
+ int *(*fp)(char *, float *, double *);
+};
+
+struct st2 {
+ struct st1 m;
+};
+
+// CHECK-LABEL: define {{(dso_local)?}} void @stparam(
+// CHECK-SAME: {{.*}} !type [[F_TSTRUCT:![0-9]+]]
+void stparam(struct st2 a, struct st2 *b) {}
+
+// CHECK-LABEL: define {{(dso_local)?}} void @stf(
+// CHECK-SAME: {{.*}} !type [[F_TVOID]]
+void stf() {
+ struct st1 St1;
+ St1.fp = qux;
+ // CHECK: call ptr {{.*}}, !callee_type [[F_TPTR_CT:![0-9]+]]
+ St1.fp(0, 0, 0);
+
+ struct st2 St2;
+ St2.m.fp = qux;
+ // CHECK: call ptr {{.*}}, !callee_type [[F_TPTR_CT:![0-9]+]]
+ St2.m.fp(0, 0, 0);
+
+ // CHECK: call void {{.*}}, !callee_type [[F_TSTRUCT_CT:![0-9]+]]
+ void (*fp_stparam)(struct st2, struct st2 *) = stparam;
+ fp_stparam(St2, &St2);
+}
+
+// ITANIUM: [[F_TVOID]] = !{i64 0, !"_ZTSFvE.generalized"}
+// ITANIUM: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
+// ITANIUM: [[F_TPRIMITIVE]] = !{i64 0, !"_ZTSFicfdE.generalized"}
+// ITANIUM: [[F_TPTR]] = !{i64 0, !"_ZTSFPiPcPfPdE.generalized"}
+// ITANIUM: [[F_TPRIMITIVE_CT]] = !{[[F_TPRIMITIVE:![0-9]+]]}
+// ITANIUM: [[F_TPTR_CT]] = !{[[F_TPTR:![0-9]+]]}
+// ITANIUM: [[F_TSTRUCT]] = !{i64 0, !"_ZTSFv3st2PS_E.generalized"}
+// ITANIUM: [[F_TSTRUCT_CT]] = !{[[F_TSTRUCT:![0-9]+]]}
+
+// MS: [[F_TVOID]] = !{i64 0, !"?6AX at Z.generalized"}
+// MS: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]}
+// MS: [[F_TPRIMITIVE]] = !{i64 0, !"?6AHDMN at Z.generalized"}
+// MS: [[F_TPTR]] = !{i64 0, !"?6APEAHPEADPEAMPEAN at Z.generalized"}
+// MS: [[F_TPRIMITIVE_CT]] = !{[[F_TPRIMITIVE:![0-9]+]]}
+// MS: [[F_TPTR_CT]] = !{[[F_TPTR:![0-9]+]]}
+// MS: [[F_TSTRUCT]] = !{i64 0, !"?6AXUst2@@PEAU0@@Z.generalized"}
+// MS: [[F_TSTRUCT_CT]] = !{[[F_TSTRUCT:![0-9]+]]}
diff --git a/clang/test/CodeGen/call-graph-section.cpp b/clang/test/CodeGen/call-graph-section.cpp
new file mode 100644
index 0000000000000..86ed3ee2337a7
--- /dev/null
+++ b/clang/test/CodeGen/call-graph-section.cpp
@@ -0,0 +1,147 @@
+// Tests that we assign appropriate identifiers to indirect calls and targets
+// specifically for C++ class and instance methods.
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
+// RUN: -emit-llvm -o %t %s
+// RUN: FileCheck --check-prefix=FT %s < %t
+// RUN: FileCheck --check-prefix=CST %s < %t
+
+////////////////////////////////////////////////////////////////////////////////
+// Class definitions (check for indirect target metadata)
+
+class Cls1 {
+public:
+ // FT-LABEL: define {{.*}} ptr @_ZN4Cls18receiverEPcPf(
+ // FT-SAME: {{.*}} !type [[F_TCLS1RECEIVER:![0-9]+]]
+ static int *receiver(char *a, float *b) { return 0; }
+};
+
+class Cls2 {
+public:
+ int *(*fp)(char *, float *);
+
+ // FT-LABEL: define {{.*}} i32 @_ZN4Cls22f1Ecfd(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F1:![0-9]+]]
+ int f1(char a, float b, double c) { return 0; }
+
+ // FT-LABEL: define {{.*}} ptr @_ZN4Cls22f2EPcPfPd(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F2:![0-9]+]]
+ int *f2(char *a, float *b, double *c) { return 0; }
+
+ // FT-LABEL: define {{.*}} void @_ZN4Cls22f3E4Cls1(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F3F4:![0-9]+]]
+ void f3(Cls1 a) {}
+
+ // FT-LABEL: define {{.*}} void @_ZN4Cls22f4E4Cls1(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F3F4]]
+ void f4(const Cls1 a) {}
+
+ // FT-LABEL: define {{.*}} void @_ZN4Cls22f5EP4Cls1(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F5:![0-9]+]]
+ void f5(Cls1 *a) {}
+
+ // FT-LABEL: define {{.*}} void @_ZN4Cls22f6EPK4Cls1(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F6:![0-9]+]]
+ void f6(const Cls1 *a) {}
+
+ // FT-LABEL: define {{.*}} void @_ZN4Cls22f7ER4Cls1(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F7:![0-9]+]]
+ void f7(Cls1 &a) {}
+
+ // FT-LABEL: define {{.*}} void @_ZN4Cls22f8ERK4Cls1(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F8:![0-9]+]]
+ void f8(const Cls1 &a) {}
+
+ // FT-LABEL: define {{.*}} void @_ZNK4Cls22f9Ev(
+ // FT-SAME: {{.*}} !type [[F_TCLS2F9:![0-9]+]]
+ void f9() const {}
+};
+
+// FT: [[F_TCLS1RECEIVER]] = !{i64 0, !"_ZTSFPiPcPfE.generalized"}
+// FT: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFicfdE.generalized"}
+// FT: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFPiPcPfPdE.generalized"}
+// FT: [[F_TCLS2F3F4]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"}
+// FT: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvP4Cls1E.generalized"}
+// FT: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvPK4Cls1E.generalized"}
+// FT: [[F_TCLS2F7]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"}
+// FT: [[F_TCLS2F8]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"}
+// FT: [[F_TCLS2F9]] = !{i64 0, !"_ZTSKFvvE.generalized"}
+
+////////////////////////////////////////////////////////////////////////////////
+// Callsites (check for indirect callsites' callee_type metadata )
+
+// CST-LABEL: define {{.*}} @_Z3foov
+void foo() {
+ Cls2 ObjCls2;
+ ObjCls2.fp = &Cls1::receiver;
+
+ // CST: call noundef ptr %{{.*}}, !callee_type [[F_TCLS1RECEIVER_CT:![0-9]+]]
+ ObjCls2.fp(0, 0);
+
+ auto fp_f1 = &Cls2::f1;
+ auto fp_f2 = &Cls2::f2;
+ auto fp_f3 = &Cls2::f3;
+ auto fp_f4 = &Cls2::f4;
+ auto fp_f5 = &Cls2::f5;
+ auto fp_f6 = &Cls2::f6;
+ auto fp_f7 = &Cls2::f7;
+ auto fp_f8 = &Cls2::f8;
+ auto fp_f9 = &Cls2::f9;
+
+ Cls2 *ObjCls2Ptr = &ObjCls2;
+ Cls1 Cls1Param;
+
+ // CST: call noundef i32 %{{.*}}, !callee_type [[F_TCLS2F1_CT:![0-9]+]]
+ (ObjCls2Ptr->*fp_f1)(0, 0, 0);
+
+ // CST: call noundef ptr %{{.*}}, !callee_type [[F_TCLS2F2_CT:![0-9]+]]
+ (ObjCls2Ptr->*fp_f2)(0, 0, 0);
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F3F4_CT:![0-9]+]]
+ (ObjCls2Ptr->*fp_f3)(Cls1Param);
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F3F4_CT:![0-9]+]]
+ (ObjCls2Ptr->*fp_f4)(Cls1Param);
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F5_CT:![0-9]+]]
+ (ObjCls2Ptr->*fp_f5)(&Cls1Param);
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F6_CT:![0-9]+]]
+ (ObjCls2Ptr->*fp_f6)(&Cls1Param);
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F7_CT:![0-9]+]]
+ (ObjCls2Ptr->*fp_f7)(Cls1Param);
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F8_CT:![0-9]+]]
+ (ObjCls2Ptr->*fp_f8)(Cls1Param);
+
+ // CST: call void %{{.*}}, !callee_type [[F_TCLS2F9_CT:![0-9]+]]
+ (ObjCls2Ptr->*fp_f9)();
+}
+
+// CST: [[F_TCLS1RECEIVER_CT]] = !{[[F_TCLS1RECEIVER:![0-9]+]]}
+// CST: [[F_TCLS1RECEIVER]] = !{i64 0, !"_ZTSFPiPcPfE.generalized"}
+
+// CST: [[F_TCLS2F1_CT]] = !{[[F_TCLS2F1:![0-9]+]]}
+// CST: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFicfdE.generalized"}
+
+// CST: [[F_TCLS2F2_CT]] = !{[[F_TCLS2F2:![0-9]+]]}
+// CST: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFPiPcPfPdE.generalized"}
+
+// CST: [[F_TCLS2F3F4_CT]] = !{[[F_TCLS2F3F4:![0-9]+]]}
+// CST: [[F_TCLS2F3F4]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"}
+
+// CST: [[F_TCLS2F5_CT]] = !{[[F_TCLS2F5:![0-9]+]]}
+// CST: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvP4Cls1E.generalized"}
+
+// CST: [[F_TCLS2F6_CT]] = !{[[F_TCLS2F6:![0-9]+]]}
+// CST: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvPK4Cls1E.generalized"}
+
+// CST: [[F_TCLS2F7_CT]] = !{[[F_TCLS2F7:![0-9]+]]}
+// CST: [[F_TCLS2F7]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"}
+
+// CST: [[F_TCLS2F8_CT]] = !{[[F_TCLS2F8:![0-9]+]]}
+// CST: [[F_TCLS2F8]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"}
+
+// CST: [[F_TCLS2F9_CT]] = !{[[F_TCLS2F9:![0-9]+]]}
+// CST: [[F_TCLS2F9]] = !{i64 0, !"_ZTSKFvvE.generalized"}
More information about the cfe-commits
mailing list