diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2182,7 +2182,13 @@ const CGFunctionInfo &Info = CGM.getTypes().arrangeCXXConstructorCall( Args, D, Type, ExtraArgs.Prefix, ExtraArgs.Suffix, PassPrototypeArgs); CGCallee Callee = CGCallee::forDirect(CalleePtr, GlobalDecl(D, Type)); - EmitCall(Info, Callee, ReturnValueSlot(), Args, nullptr, false, Loc); + llvm::CallBase *CallOrInvoke = nullptr; + EmitCall(Info, Callee, ReturnValueSlot(), Args, &CallOrInvoke, false, Loc); + + // Set type identifier metadata of indirect calls for call graph section. + if (CGM.getCodeGenOpts().CallGraphSection && CallOrInvoke && + CallOrInvoke->isIndirectCall()) + CGM.CreateFunctionTypeMetadataForIcall(D->getType(), CallOrInvoke); // Generate vtable assumptions if we're constructing a complete object // with a vtable. We don't do this for base subobjects for two reasons: diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5311,6 +5311,11 @@ CalleeDecl); } + // Set type identifier metadata of indirect calls for call graph section. + if (CGM.getCodeGenOpts().CallGraphSection && CallOrInvoke && + CallOrInvoke->isIndirectCall()) + CGM.CreateFunctionTypeMetadataForIcall(QualType(FnType, 0), CallOrInvoke); + return Call; } diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -86,9 +86,17 @@ *this, MD, This, ImplicitParam, ImplicitParamTy, CE, Args, RtlArgs); auto &FnInfo = CGM.getTypes().arrangeCXXMethodCall( Args, FPT, CallInfo.ReqArgs, CallInfo.PrefixSize); - return EmitCall(FnInfo, Callee, ReturnValue, Args, nullptr, - CE && CE == MustTailCall, - CE ? CE->getExprLoc() : SourceLocation()); + llvm::CallBase *CallOrInvoke = nullptr; + auto Call = EmitCall(FnInfo, Callee, ReturnValue, Args, &CallOrInvoke, + CE && CE == MustTailCall, + CE ? CE->getExprLoc() : SourceLocation()); + + // Set type identifier metadata of indirect calls for call graph section. + if (CGM.getCodeGenOpts().CallGraphSection && CallOrInvoke && + CallOrInvoke->isIndirectCall()) + CGM.CreateFunctionTypeMetadataForIcall(MD->getType(), CallOrInvoke); + + return Call; } RValue CodeGenFunction::EmitCXXDestructorCall( @@ -112,9 +120,18 @@ CallArgList Args; commonEmitCXXMemberOrOperatorCall(*this, DtorDecl, This, ImplicitParam, ImplicitParamTy, CE, Args, nullptr); - return EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(Dtor), Callee, - ReturnValueSlot(), Args, nullptr, CE && CE == MustTailCall, - CE ? CE->getExprLoc() : SourceLocation{}); + llvm::CallBase *CallOrInvoke = nullptr; + auto Call = + EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(Dtor), Callee, + ReturnValueSlot(), Args, &CallOrInvoke, CE && CE == MustTailCall, + CE ? CE->getExprLoc() : SourceLocation{}); + + // Set type identifier metadata of indirect calls for call graph section. + if (CGM.getCodeGenOpts().CallGraphSection && CallOrInvoke && + CallOrInvoke->isIndirectCall()) + CGM.CreateFunctionTypeMetadataForIcall(DtorDecl->getType(), CallOrInvoke); + + return Call; } RValue CodeGenFunction::EmitCXXPseudoDestructorExpr( @@ -471,10 +488,18 @@ // And the rest of the call args EmitCallArgs(Args, FPT, E->arguments()); - return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required, - /*PrefixSize=*/0), - Callee, ReturnValue, Args, nullptr, E == MustTailCall, - E->getExprLoc()); + llvm::CallBase *CallOrInvoke = nullptr; + auto Call = EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required, + /*PrefixSize=*/0), + Callee, ReturnValue, Args, &CallOrInvoke, + E == MustTailCall, E->getExprLoc()); + + // Set type identifier metadata of indirect calls for call graph section. + if (CGM.getCodeGenOpts().CallGraphSection && CallOrInvoke && + CallOrInvoke->isIndirectCall()) + CGM.CreateFunctionTypeMetadataForIcall(QualType(FPT, 0), CallOrInvoke); + + return Call; } RValue diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1406,6 +1406,10 @@ void CreateFunctionTypeMetadataForIcall(const FunctionDecl *FD, llvm::Function *F); + /// Create and attach type metadata to the given call. + void CreateFunctionTypeMetadataForIcall(const QualType &QT, + llvm::CallBase *CB); + /// Whether this function's return type has no side effects, and thus may /// be trivially discarded if it is unused. bool MayDropFunctionReturn(const ASTContext &Context, QualType ReturnType); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2057,7 +2057,17 @@ void CodeGenModule::CreateFunctionTypeMetadataForIcall(const FunctionDecl *FD, llvm::Function *F) { - // Only if we are checking indirect calls. + bool EmittedMDIdGeneralized = false; + if (CodeGenOpts.CallGraphSection && + (!F->hasLocalLinkage() || + F->getFunction().hasAddressTaken(nullptr, /* IgnoreCallbackUses */ true, + /* IgnoreAssumeLikeCalls */ true, + /* IgnoreLLVMUsed */ false))) { + F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType())); + EmittedMDIdGeneralized = true; + } + + // Add additional metadata only if we are checking indirect calls with CFI. if (!LangOpts.Sanitize.has(SanitizerKind::CFIICall)) return; @@ -2068,7 +2078,9 @@ llvm::Metadata *MD = CreateMetadataIdentifierForType(FD->getType()); F->addTypeMetadata(0, MD); - F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType())); + // Add the generalized identifier if not added already. + if (!EmittedMDIdGeneralized) + F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType())); // Emit a hash-based bit set entry for cross-DSO calls. if (CodeGenOpts.SanitizeCfiCrossDso) @@ -2076,6 +2088,17 @@ F->addTypeMetadata(0, llvm::ConstantAsMetadata::get(CrossDsoTypeId)); } +void CodeGenModule::CreateFunctionTypeMetadataForIcall(const QualType &QT, + llvm::CallBase *CB) { + // Only if needed for call graph section and only for indirect calls. + if (!(CodeGenOpts.CallGraphSection && CB && CB->isIndirectCall())) + return; + + auto *MD = CreateMetadataIdentifierGeneralized(QT); + auto *MDN = llvm::MDNode::get(getLLVMContext(), MD); + CB->setMetadata(llvm::LLVMContext::MD_type, MDN); +} + void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F, bool IsIncompleteFunction, bool IsThunk) { @@ -2150,7 +2173,8 @@ // are non-canonical then we need type metadata in order to produce the local // jump table. if (!CodeGenOpts.SanitizeCfiCrossDso || - !CodeGenOpts.SanitizeCfiCanonicalJumpTables) + !CodeGenOpts.SanitizeCfiCanonicalJumpTables || + CodeGenOpts.CallGraphSection) CreateFunctionTypeMetadataForIcall(FD, F); if (getLangOpts().OpenMP && FD->hasAttr()) diff --git a/clang/test/CodeGen/call-graph-section-1.cpp b/clang/test/CodeGen/call-graph-section-1.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/call-graph-section-1.cpp @@ -0,0 +1,119 @@ +// 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 -fcall-graph-section -S \ +// 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-DAG: define {{.*}} i32* @_ZN4Cls18receiverEPcPf({{.*}} !type [[F_TCLS1RECEIVER:![0-9]+]] + static int *receiver(char *a, float *b) { return 0; } +}; + +class Cls2 { +public: + int *(*fp)(char *, float *); + + // FT-DAG: define {{.*}} i32 @_ZN4Cls22f1Ecfd({{.*}} !type [[F_TCLS2F1:![0-9]+]] + int f1(char a, float b, double c) { return 0; } + + // FT-DAG: define {{.*}} i32* @_ZN4Cls22f2EPcPfPd({{.*}} !type [[F_TCLS2F2:![0-9]+]] + int *f2(char *a, float *b, double *c) { return 0; } + + // FT-DAG: define {{.*}} void @_ZN4Cls22f3E4Cls1({{.*}} !type [[F_TCLS2F3F4:![0-9]+]] + void f3(Cls1 a) {} + + // FT-DAG: define {{.*}} void @_ZN4Cls22f4E4Cls1({{.*}} !type [[F_TCLS2F3F4]] + void f4(const Cls1 a) {} + + // FT-DAG: define {{.*}} void @_ZN4Cls22f5EP4Cls1({{.*}} !type [[F_TCLS2F5:![0-9]+]] + void f5(Cls1 *a) {} + + // FT-DAG: define {{.*}} void @_ZN4Cls22f6EPK4Cls1({{.*}} !type [[F_TCLS2F6:![0-9]+]] + void f6(const Cls1 *a) {} + + // FT-DAG: define {{.*}} void @_ZN4Cls22f7ER4Cls1({{.*}} !type [[F_TCLS2F7:![0-9]+]] + void f7(Cls1 &a) {} + + // FT-DAG: define {{.*}} void @_ZN4Cls22f8ERK4Cls1({{.*}} !type [[F_TCLS2F8:![0-9]+]] + void f8(const Cls1 &a) {} + + // FT-DAG: define {{.*}} void @_ZNK4Cls22f9Ev({{.*}} !type [[F_TCLS2F9:![0-9]+]] + void f9() const {} +}; + +// FT-DAG: [[F_TCLS1RECEIVER]] = !{i64 0, !"_ZTSFPvS_S_E.generalized"} +// FT-DAG: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFPvS_S_S_E.generalized"} +// FT-DAG: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFicfdE.generalized"} +// FT-DAG: [[F_TCLS2F3F4]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"} +// FT-DAG: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvPvE.generalized"} +// FT-DAG: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvPKvE.generalized"} +// FT-DAG: [[F_TCLS2F7]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"} +// FT-DAG: [[F_TCLS2F8]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"} +// FT-DAG: [[F_TCLS2F9]] = !{i64 0, !"_ZTSKFvvE.generalized"} + +//////////////////////////////////////////////////////////////////////////////// +// Callsites (check for indirect callsite metadata) + +// CST-LABEL: define {{.*}} @_Z3foov +void foo() { + Cls2 ObjCls2; + ObjCls2.fp = &Cls1::receiver; + // CST: call i32* {{.*}} !type [[CS_TCLS1RECEIVER:![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 i32 %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F1:![0-9]+]] + (ObjCls2Ptr->*fp_f1)(0, 0, 0); + + // CST: call i32* %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F2:![0-9]+]] + (ObjCls2Ptr->*fp_f2)(0, 0, 0); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F3F4:![0-9]+]] + (ObjCls2Ptr->*fp_f3)(Cls1Param); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F3F4]] + (ObjCls2Ptr->*fp_f4)(Cls1Param); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F5:![0-9]+]] + (ObjCls2Ptr->*fp_f5)(&Cls1Param); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F6:![0-9]+]] + (ObjCls2Ptr->*fp_f6)(&Cls1Param); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F7:![0-9]+]] + (ObjCls2Ptr->*fp_f7)(Cls1Param); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F8:![0-9]+]] + (ObjCls2Ptr->*fp_f8)(Cls1Param); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F9:![0-9]+]] + (ObjCls2Ptr->*fp_f9)(); +} + +// CST-DAG: [[CS_TCLS1RECEIVER]] = !{!"_ZTSFPvS_S_E.generalized"} +// CST-DAG: [[CS_TCLS2F1]] = !{!"_ZTSFicfdE.generalized"} +// CST-DAG: [[CS_TCLS2F2]] = !{!"_ZTSFPvS_S_S_E.generalized"} +// CST-DAG: [[CS_TCLS2F3F4]] = !{!"_ZTSFv4Cls1E.generalized"} +// CST-DAG: [[CS_TCLS2F5]] = !{!"_ZTSFvPvE.generalized"} +// CST-DAG: [[CS_TCLS2F6]] = !{!"_ZTSFvPKvE.generalized"} +// CST-DAG: [[CS_TCLS2F7]] = !{!"_ZTSFvR4Cls1E.generalized"} +// CST-DAG: [[CS_TCLS2F8]] = !{!"_ZTSFvRK4Cls1E.generalized"} +// CST-DAG: [[CS_TCLS2F9]] = !{!"_ZTSKFvvE.generalized"} diff --git a/clang/test/CodeGen/call-graph-section-2.cpp b/clang/test/CodeGen/call-graph-section-2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/call-graph-section-2.cpp @@ -0,0 +1,103 @@ +// Tests that we assign appropriate identifiers to indirect calls and targets +// specifically for C++ templates. + +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fcall-graph-section -S \ +// RUN: -emit-llvm -o %t %s +// RUN: FileCheck --check-prefix=FT %s < %t +// RUN: FileCheck --check-prefix=CST %s < %t +// RUN: FileCheck --check-prefix=CHECK %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: define {{.*}} void @_ZN4Cls2I4Cls1E2f1Ev({{.*}} !type [[F_TCLS2F1:![0-9]+]] + void f1() {} + + // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f2ES0_({{.*}} !type [[F_TCLS2F2:![0-9]+]] + void f2(T a) {} + + // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f3EPS0_({{.*}} !type [[F_TCLS2F3:![0-9]+]] + void f3(T *a) {} + + // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f4EPKS0_({{.*}} !type [[F_TCLS2F4:![0-9]+]] + void f4(const T *a) {} + + // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f5ERS0_({{.*}} !type [[F_TCLS2F5:![0-9]+]] + void f5(T &a) {} + + // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f6ERKS0_({{.*}} !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-DAG: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFvvE.generalized"} +// FT-DAG: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"} +// FT-DAG: [[F_TCLS2F3]] = !{i64 0, !"_ZTSFvPvE.generalized"} +// FT-DAG: [[F_TCLS2F4]] = !{i64 0, !"_ZTSFvPKvE.generalized"} +// FT-DAG: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"} +// FT-DAG: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"} + +//////////////////////////////////////////////////////////////////////////////// +// Callsites (check for indirect callsite metadata) + +template +T *T_func(T a, T *b, const T *c, T &d, const T &e) { return b; } + +// CST-LABEL: define {{.*}} @_Z3foov +void foo() { + // Methods for Cls2 is checked above within the template description. + Cls2 Obj; + + // CHECK-DAG: define {{.*}} @_Z6T_funcI4Cls1EPT_S1_S2_PKS1_RS1_RS3_({{.*}} !type [[F_TFUNC_CLS1:![0-9]+]] + // CHECK-DAG: [[F_TFUNC_CLS1]] = !{i64 0, !"_ZTSFPv4Cls1S_PKvRS0_RKS0_E.generalized"} + Obj.fp = T_func; + Cls1 Cls1Obj; + + // CHECK-DAG: call %class.Cls1* {{.*}}, !type [[CS_TFUNC_CLS1:![0-9]+]] + // CHECK-DAG: [[CS_TFUNC_CLS1]] = !{!"_ZTSFPv4Cls1S_PKvRS0_RKS0_E.generalized"} + 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 %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F1:![0-9]+]] + (Obj2Ptr->*fp_f1)(); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F2:![0-9]+]] + (Obj2Ptr->*fp_f2)(Cls1Obj); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F3:![0-9]+]] + (Obj2Ptr->*fp_f3)(&Cls1Obj); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F4:![0-9]+]] + (Obj2Ptr->*fp_f4)(&Cls1Obj); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F5:![0-9]+]] + (Obj2Ptr->*fp_f5)(Cls1Obj); + + // CST: call void %{{.*}}(%class.Cls2*{{.*}} !type [[CS_TCLS2F6:![0-9]+]] + (Obj2Ptr->*fp_f6)(Cls1Obj); +} + +// CST-DAG: [[CS_TCLS2F1]] = !{!"_ZTSFvvE.generalized"} +// CST-DAG: [[CS_TCLS2F2]] = !{!"_ZTSFv4Cls1E.generalized"} +// CST-DAG: [[CS_TCLS2F3]] = !{!"_ZTSFvPvE.generalized"} +// CST-DAG: [[CS_TCLS2F4]] = !{!"_ZTSFvPKvE.generalized"} +// CST-DAG: [[CS_TCLS2F5]] = !{!"_ZTSFvR4Cls1E.generalized"} +// CST-DAG: [[CS_TCLS2F6]] = !{!"_ZTSFvRK4Cls1E.generalized"} diff --git a/clang/test/CodeGen/call-graph-section-3.cpp b/clang/test/CodeGen/call-graph-section-3.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/call-graph-section-3.cpp @@ -0,0 +1,54 @@ +// Tests that we assign appropriate identifiers to indirect calls and targets +// specifically for virtual methods. + +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fcall-graph-section -S \ +// 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-DAG: define {{.*}} @_ZN4Base2vfEPc({{.*}} !type [[F_TVF:![0-9]+]] + virtual int vf(char *a) { return 0; }; +}; + +class Derived : public Base { +public: + // FT-DAG: define {{.*}} @_ZN7Derived2vfEPc({{.*}} !type [[F_TVF]] + int vf(char *a) override { return 1; }; +}; + +// FT-DAG: [[F_TVF]] = !{i64 0, !"_ZTSFiPvE.generalized"} + +//////////////////////////////////////////////////////////////////////////////// +// Callsites (check for indirect callsite metadata) + +// 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 i32 %{{.*}}(%class.Base* {{.*}} !type [[CS_TVF:![0-9]+]] + (Bptr->*FpBaseVf)(0); + + // CST: call i32 %{{.*}}(%class.Base* {{.*}} !type [[CS_TVF]] + (BptrToD->*FpBaseVf)(0); + + // CST: call i32 %{{.*}}(%class.Base* {{.*}} !type [[CS_TVF]] + (Dptr->*FpBaseVf)(0); + + // CST: call i32 %{{.*}}(%class.Derived* {{.*}} !type [[CS_TVF]] + (Dptr->*FpDerivedVf)(0); +} + +// CST-DAG: [[CS_TVF]] = !{!"_ZTSFiPvE.generalized"} diff --git a/clang/test/CodeGen/call-graph-section.c b/clang/test/CodeGen/call-graph-section.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/call-graph-section.c @@ -0,0 +1,87 @@ +// Tests that we assign appropriate identifiers to indirect calls and targets. + +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fcall-graph-section -S \ +// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,ITANIUM %s + +// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fcall-graph-section -S \ +// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,MS %s + +// CHECK-DAG: define {{(dso_local)?}} void @foo({{.*}} !type [[F_TVOID:![0-9]+]] +void foo() { +} + +// CHECK-DAG: define {{(dso_local)?}} void @bar({{.*}} !type [[F_TVOID]] +void bar() { + void (*fp)() = foo; + // CHECK: call {{.*}} !type [[CS_TVOID:![0-9]+]] + fp(); +} + +// CHECK-DAG: define {{(dso_local)?}} i32 @baz({{.*}} !type [[F_TPRIMITIVE:![0-9]+]] +int baz(char a, float b, double c) { + return 1; +} + +// CHECK-DAG: define {{(dso_local)?}} i32* @qux({{.*}} !type [[F_TPTR:![0-9]+]] +int *qux(char *a, float *b, double *c) { + return 0; +} + +// CHECK-DAG: define {{(dso_local)?}} void @corge({{.*}} !type [[F_TVOID]] +void corge() { + int (*fp_baz)(char, float, double) = baz; + // CHECK: call i32 {{.*}}, !type [[CS_TPRIMITIVE:![0-9]+]] + fp_baz('a', .0f, .0); + + int *(*fp_qux)(char *, float *, double *) = qux; + // CHECK: call i32* {{.*}}, !type [[CS_TPTR:![0-9]+]] + fp_qux(0, 0, 0); +} + +struct st1 { + int *(*fp)(char *, float *, double *); +}; + +struct st2 { + struct st1 m; +}; + +// CHECK-DAG: define {{(dso_local)?}} void @stparam({{.*}} !type [[F_TSTRUCT:![0-9]+]] +void stparam(struct st2 a, struct st2 *b) {} + +// CHECK-DAG: define {{(dso_local)?}} void @stf({{.*}} !type [[F_TVOID]] +void stf() { + struct st1 St1; + St1.fp = qux; + // CHECK: call i32* {{.*}}, !type [[CS_TPTR]] + St1.fp(0, 0, 0); + + struct st2 St2; + St2.m.fp = qux; + // CHECK: call i32* {{.*}}, !type [[CS_TPTR]] + St2.m.fp(0, 0, 0); + + // CHECK: call void {{.*}} !type [[CS_TSTRUCT:![0-9]+]] + void (*fp_stparam)(struct st2, struct st2 *) = stparam; + fp_stparam(St2, &St2); +} + +// ITANIUM-DAG: [[F_TVOID]] = !{i64 0, !"_ZTSFvE.generalized"} +// ITANIUM-DAG: [[CS_TVOID]] = !{!"_ZTSFvE.generalized"} +// MS-DAG: [[F_TVOID]] = !{i64 0, !"?6AX@Z.generalized"} +// MS-DAG: [[CS_TVOID]] = !{!"?6AX@Z.generalized"} + +// ITANIUM-DAG: [[F_TPRIMITIVE]] = !{i64 0, !"_ZTSFicfdE.generalized"} +// ITANIUM-DAG: [[CS_TPRIMITIVE]] = !{!"_ZTSFicfdE.generalized"} +// MS-DAG: [[F_TPRIMITIVE]] = !{i64 0, !"?6AHDMN@Z.generalized"} +// MS-DAG: [[CS_TPRIMITIVE]] = !{!"?6AHDMN@Z.generalized"} + +// ITANIUM-DAG: [[F_TPTR]] = !{i64 0, !"_ZTSFPvS_S_S_E.generalized"} +// ITANIUM-DAG: [[CS_TPTR]] = !{!"_ZTSFPvS_S_S_E.generalized"} +// MS-DAG: [[F_TPTR]] = !{i64 0, !"?6APEAXPEAX00@Z.generalized"} +// MS-DAG: [[CS_TPTR]] = !{!"?6APEAXPEAX00@Z.generalized"} + +// ITANIUM-DAG: [[F_TSTRUCT]] = !{i64 0, !"_ZTSFv3st2PvE.generalized"} +// MS-DAG: [[F_TSTRUCT]] = !{i64 0, !"?6AXUst2@@PEAX@Z.generalized"} +// ITANIUM-DAG: [[CS_TSTRUCT]] = !{!"_ZTSFv3st2PvE.generalized"} +// MS-DAG: [[CS_TSTRUCT]] = !{!"?6AXUst2@@PEAX@Z.generalized"} \ No newline at end of file