https://github.com/Prabhuk created https://github.com/llvm/llvm-project/pull/161598
This patch adds clang CodeGen option to emit callgraph section. The `-fcall-graph-section` Driver flag is introduced to control the `CallGraphSection` CodeGen option. >From 7cc1234028c9f114a625080ebf4a7ad11bbf342a Mon Sep 17 00:00:00 2001 From: prabhukr <[email protected]> Date: Tue, 11 Mar 2025 20:15:36 +0000 Subject: [PATCH] [clang] All Clang Changes This patch adds clang CodeGen option to emit callgraph section. The `-fcall-graph-section` Driver flag is introduced to control the `CallGraphSection` CodeGen option. --- clang/include/clang/Basic/CodeGenOptions.def | 2 + clang/include/clang/Driver/Options.td | 6 + clang/lib/CodeGen/BackendUtil.cpp | 1 + clang/lib/CodeGen/CGCall.cpp | 18 ++- clang/lib/CodeGen/CodeGenModule.cpp | 44 +++++- clang/lib/CodeGen/CodeGenModule.h | 3 + clang/lib/Driver/ToolChains/Clang.cpp | 4 + clang/lib/Driver/ToolChains/CommonArgs.cpp | 5 + .../CodeGen/call-graph-section-templates.cpp | 114 +++++++++++++++ .../call-graph-section-virtual-methods.cpp | 55 +++++++ clang/test/CodeGen/call-graph-section.c | 83 +++++++++++ clang/test/CodeGen/call-graph-section.cpp | 138 ++++++++++++++++++ clang/test/Driver/call-graph-section.c | 5 + 13 files changed, 470 insertions(+), 8 deletions(-) create mode 100644 clang/test/CodeGen/call-graph-section-templates.cpp create mode 100644 clang/test/CodeGen/call-graph-section-virtual-methods.cpp create mode 100644 clang/test/CodeGen/call-graph-section.c create mode 100644 clang/test/CodeGen/call-graph-section.cpp create mode 100644 clang/test/Driver/call-graph-section.c diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 872f73ebf3810..a8316a3b2c814 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -72,6 +72,8 @@ CODEGENOPT(EnableNoundefAttrs, 1, 0, Benign) ///< Enable emitting `noundef` attr CODEGENOPT(DebugPassManager, 1, 0, Benign) ///< Prints debug information for the new ///< pass manager. CODEGENOPT(DisableRedZone , 1, 0, Benign) ///< Set when -mno-red-zone is enabled. +CODEGENOPT(CallGraphSection, 1, 0, Benign) ///< Emit a call graph section into the + ///< object file. CODEGENOPT(EmitCallSiteInfo, 1, 0, Benign) ///< Emit call site info only in the case of ///< '-g' + 'O>0' level. CODEGENOPT(IndirectTlsSegRefs, 1, 0, Benign) ///< Set when -mno-tls-direct-seg-refs diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 6245cf33a0719..e1d07fe3a837d 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4512,6 +4512,12 @@ defm data_sections : BoolFOption<"data-sections", PosFlag<SetTrue, [], [ClangOption, CC1Option], "Place each data in its own section">, NegFlag<SetFalse>>; +defm experimental_call_graph_section + : BoolFOption<"experimental-call-graph-section", CodeGenOpts<"CallGraphSection">, + DefaultFalse, + PosFlag<SetTrue, [], [ClangOption, CC1Option], + "Emit a call graph section">, + NegFlag<SetFalse>>; defm stack_size_section : BoolFOption<"stack-size-section", CodeGenOpts<"StackSizeSection">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption, CC1Option], diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 64f1917739e12..8d5e06a3cb586 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -463,6 +463,7 @@ static bool initTargetOptions(const CompilerInstance &CI, Options.StackUsageOutput = CodeGenOpts.StackUsageOutput; Options.EmitAddrsig = CodeGenOpts.Addrsig; Options.ForceDwarfFrameSection = CodeGenOpts.ForceDwarfFrameSection; + Options.EmitCallGraphSection = CodeGenOpts.CallGraphSection; Options.EmitCallSiteInfo = CodeGenOpts.EmitCallSiteInfo; Options.EnableAIXExtendedAltivecABI = LangOpts.EnableAIXExtendedAltivecABI; Options.XRayFunctionIndex = CodeGenOpts.XRayFunctionIndex; diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index a931ce476b8ae..e6aa7d4bde671 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5945,8 +5945,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 f6f7f22a09004..1e03e0b06bddc 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2836,8 +2836,9 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, // In the cross-dso CFI mode with canonical jump tables, we want !type // attributes on definitions only. - if (CodeGenOpts.SanitizeCfiCrossDso && - CodeGenOpts.SanitizeCfiCanonicalJumpTables) { + if ((CodeGenOpts.SanitizeCfiCrossDso && + CodeGenOpts.SanitizeCfiCanonicalJumpTables) || + CodeGenOpts.CallGraphSection) { if (auto *FD = dyn_cast<FunctionDecl>(D)) { // Skip available_externally functions. They won't be codegen'ed in the // current module anyway. @@ -3049,9 +3050,21 @@ 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::createFunctionTypeMetadataForIcall(const FunctionDecl *FD, llvm::Function *F) { - // Only if we are checking indirect calls. + if (CodeGenOpts.CallGraphSection && !hasExistingGeneralizedTypeMD(F) && + (!F->hasLocalLinkage() || + F->getFunction().hasAddressTaken(nullptr, /*IgnoreCallbackUses=*/true, + /*IgnoreAssumeLikeCalls=*/true, + /*IgnoreLLVMUsed=*/false))) + F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType())); + + // Add additional metadata only if we are checking indirect calls with CFI. if (!LangOpts.Sanitize.has(SanitizerKind::CFIICall)) return; @@ -3064,10 +3077,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) @@ -3075,6 +3090,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); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 3971b296b3f80..a323621136c90 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1644,6 +1644,9 @@ class CodeGenModule : public CodeGenTypeCache { void createFunctionTypeMetadataForIcall(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/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index adaa6b3005577..aa459fbf7d370 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6447,6 +6447,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(A->getValue()); } + if (Args.hasFlag(options::OPT_fexperimental_call_graph_section, + options::OPT_fno_experimental_call_graph_section, false)) + CmdArgs.push_back("-fexperimental-call-graph-section"); + Args.addOptInFlag(CmdArgs, options::OPT_fstack_size_section, options::OPT_fno_stack_size_section); diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index cce4f6487c0bd..d7263c2d52c77 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1272,6 +1272,11 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args, CmdArgs.push_back( Args.MakeArgString(Twine(PluginOptPrefix) + "-stack-size-section")); + if (Args.hasFlag(options::OPT_fexperimental_call_graph_section, + options::OPT_fno_experimental_call_graph_section, false)) + CmdArgs.push_back( + Args.MakeArgString(Twine(PluginOptPrefix) + "-call-graph-section")); + // Setup statistics file output. SmallString<128> StatsFile = getStatsFileName(Args, Output, *Input, D); if (!StatsFile.empty()) 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..43be48e94125b --- /dev/null +++ b/clang/test/CodeGen/call-graph-section-templates.cpp @@ -0,0 +1,114 @@ +// 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: 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 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 +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: define {{.*}} @_Z6T_funcI4Cls1EPT_S1_S2_PKS1_RS1_RS3_({{.*}} !type [[F_TFUNC_CLS1:![0-9]+]] +// CST-DAG: [[F_TFUNC_CLS1_CT]] = !{[[F_TFUNC_CLS1:![0-9]+]]} +// CST-DAG: [[F_TFUNC_CLS1]] = !{i64 0, !"_ZTSFPv4Cls1S_PKvRS0_RKS0_E.generalized"} + +// CST-DAG: [[F_TCLS2F1_CT]] = !{[[F_TCLS2F1:![0-9]+]]} +// CST-DAG: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFvvE.generalized"} + +// CST-DAG: [[F_TCLS2F2_CT]] = !{[[F_TCLS2F2:![0-9]+]]} +// CST-DAG: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"} + +// CST-DAG: [[F_TCLS2F3_CT]] = !{[[F_TCLS2F3:![0-9]+]]} +// CST-DAG: [[F_TCLS2F3]] = !{i64 0, !"_ZTSFvPvE.generalized"} + +// CST-DAG: [[F_TCLS2F4_CT]] = !{[[F_TCLS2F4:![0-9]+]]} +// CST-DAG: [[F_TCLS2F4]] = !{i64 0, !"_ZTSFvPKvE.generalized"} + +// CST-DAG: [[F_TCLS2F5_CT]] = !{[[F_TCLS2F5:![0-9]+]]} +// CST-DAG: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"} + +// CST-DAG: [[F_TCLS2F6_CT]] = !{[[F_TCLS2F6:![0-9]+]]} +// CST-DAG: [[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..aacd274dadbed --- /dev/null +++ b/clang/test/CodeGen/call-graph-section-virtual-methods.cpp @@ -0,0 +1,55 @@ +// 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-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 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-DAG: [[F_TVF_CT]] = !{[[F_TVF:![0-9]+]]} + // CST-DAG: [[F_TVF]] = !{i64 0, !"_ZTSFiPvE.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..3579b5551bdf8 --- /dev/null +++ b/clang/test/CodeGen/call-graph-section.c @@ -0,0 +1,83 @@ +// 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-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 {{.*}}, !callee_type [[F_TVOID_CT:![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)?}} ptr @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 {{.*}}, !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-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 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); +} + +// CHECK-DAG: [[F_TVOID_CT]] = !{[[F_TVOID:![0-9]+]]} +// ITANIUM-DAG: [[F_TVOID]] = !{i64 0, !"_ZTSFvE.generalized"} +// MS-DAG: [[F_TVOID]] = !{i64 0, !"[email protected]"} + +// CHECK-DAG: [[F_TPRIMITIVE_CT]] = !{[[F_TPRIMITIVE:![0-9]+]]} +// ITANIUM-DAG: [[F_TPRIMITIVE]] = !{i64 0, !"_ZTSFicfdE.generalized"} +// MS-DAG: [[F_TPRIMITIVE]] = !{i64 0, !"[email protected]"} + +// CHECK-DAG: [[F_TPTR_CT]] = !{[[F_TPTR:![0-9]+]]} +// ITANIUM-DAG: [[F_TPTR]] = !{i64 0, !"_ZTSFPvS_S_S_E.generalized"} +// MS-DAG: [[F_TPTR]] = !{i64 0, !"[email protected]"} + +// CHECK-DAG: [[F_TSTRUCT_CT]] = !{[[F_TSTRUCT:![0-9]+]]} +// ITANIUM-DAG: [[F_TSTRUCT]] = !{i64 0, !"_ZTSFv3st2PvE.generalized"} +// MS-DAG: [[F_TSTRUCT]] = !{i64 0, !"?6AXUst2@@[email protected]"} diff --git a/clang/test/CodeGen/call-graph-section.cpp b/clang/test/CodeGen/call-graph-section.cpp new file mode 100644 index 0000000000000..467fdd111b71d --- /dev/null +++ b/clang/test/CodeGen/call-graph-section.cpp @@ -0,0 +1,138 @@ +// 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-DAG: define {{.*}} ptr @_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 {{.*}} ptr @_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 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-DAG: [[F_TCLS1RECEIVER_CT]] = !{[[F_TCLS1RECEIVER:![0-9]+]]} +// CST-DAG: [[F_TCLS1RECEIVER]] = !{i64 0, !"_ZTSFPvS_S_E.generalized"} + +// CST-DAG: [[F_TCLS2F2_CT]] = !{[[F_TCLS2F2:![0-9]+]]} +// CST-DAG: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFPvS_S_S_E.generalized"} + +// CST-DAG: [[F_TCLS2F1_CT]] = !{[[F_TCLS2F1:![0-9]+]]} +// CST-DAG: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFicfdE.generalized"} + +// CST-DAG: [[F_TCLS2F3F4_CT]] = !{[[F_TCLS2F3F4:![0-9]+]]} +// CST-DAG: [[F_TCLS2F3F4]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"} + +// CST-DAG: [[F_TCLS2F5_CT]] = !{[[F_TCLS2F5:![0-9]+]]} +// CST-DAG: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvPvE.generalized"} + +// CST-DAG: [[F_TCLS2F6_CT]] = !{[[F_TCLS2F6:![0-9]+]]} +// CST-DAG: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvPKvE.generalized"} + +// CST-DAG: [[F_TCLS2F7_CT]] = !{[[F_TCLS2F7:![0-9]+]]} +// CST-DAG: [[F_TCLS2F7]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"} + +// CST-DAG: [[F_TCLS2F8_CT]] = !{[[F_TCLS2F8:![0-9]+]]} +// CST-DAG: [[F_TCLS2F8]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"} + +// CST-DAG: [[F_TCLS2F9_CT]] = !{[[F_TCLS2F9:![0-9]+]]} +// CST-DAG: [[F_TCLS2F9]] = !{i64 0, !"_ZTSKFvvE.generalized"} diff --git a/clang/test/Driver/call-graph-section.c b/clang/test/Driver/call-graph-section.c new file mode 100644 index 0000000000000..06857993585ae --- /dev/null +++ b/clang/test/Driver/call-graph-section.c @@ -0,0 +1,5 @@ +// RUN: %clang -### -fexperimental-call-graph-section %s 2>&1 | FileCheck --check-prefix=CALL-GRAPH-SECTION %s +// RUN: %clang -### -fexperimental-call-graph-section -fno-call-graph-section %s 2>&1 | FileCheck --check-prefix=NO-CALL-GRAPH-SECTION %s + +// CALL-GRAPH-SECTION: "-fexperimental-call-graph-section" +// NO-CALL-GRAPH-SECTION-NOT: "-fexperimental-call-graph-section" _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
