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 c5eb14e329315..2b93b8378ad58 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2851,6 +2851,11 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, } } + if (CodeGenOpts.CallGraphSection) { + if (auto *FD = dyn_cast(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. @@ -3054,6 +3059,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. @@ -3069,10 +3094,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) @@ -3080,6 +3107,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); @@ -3215,6 +3257,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 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 +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 is checked above within the template description. + Cls2 Obj; + + Obj.fp = T_func; + 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::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 *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@Z.generalized"} +// MS: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]} +// MS: [[F_TPRIMITIVE]] = !{i64 0, !"?6AHDMN@Z.generalized"} +// MS: [[F_TPTR]] = !{i64 0, !"?6APEAHPEADPEAMPEAN@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"}