Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 49 additions & 4 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2851,6 +2851,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.
Expand Down Expand Up @@ -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))
Comment on lines +3076 to +3078
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arsenm @ilovepi --

The "IgnoreCallbackUses" here refers to the callback attribute:
https://clang.llvm.org/docs/AttributeReference.html#callback

As part of my LLVM change (https://github.com/llvm/llvm-project/blob/main/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp#L1702), I added a test that verifies hasAddressTaken call handles callback functions correctly https://github.com/llvm/llvm-project/blob/main/llvm/test/CodeGen/X86/call-graph-section-addrtaken.ll

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a new test case to cover the hasAddressTaken case. This test also covers callback function handling.

F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType()));
}

void CodeGenModule::createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
llvm::Function *F) {
// Only if we are checking indirect calls.
Expand All @@ -3069,17 +3094,34 @@ 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)
if (auto CrossDsoTypeId = CreateCrossDsoCfiTypeId(MD))
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);
Expand Down Expand Up @@ -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);

Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
114 changes: 114 additions & 0 deletions clang/test/CodeGen/call-graph-section-templates.cpp
Original file line number Diff line number Diff line change
@@ -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, !"_ZTSFvP4Cls1E.generalized"}
// FT-DAG: [[F_TCLS2F4]] = !{i64 0, !"_ZTSFvPK4Cls1E.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, !"_ZTSFP4Cls1S_S0_PKS_RS_RS1_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, !"_ZTSFvP4Cls1E.generalized"}

// CST-DAG: [[F_TCLS2F4_CT]] = !{[[F_TCLS2F4:![0-9]+]]}
// CST-DAG: [[F_TCLS2F4]] = !{i64 0, !"_ZTSFvPK4Cls1E.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"}
55 changes: 55 additions & 0 deletions clang/test/CodeGen/call-graph-section-virtual-methods.cpp
Original file line number Diff line number Diff line change
@@ -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, !"_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-DAG: [[F_TVF_CT]] = !{[[F_TVF:![0-9]+]]}
// CST-DAG: [[F_TVF]] = !{i64 0, !"_ZTSFiPcE.generalized"}
83 changes: 83 additions & 0 deletions clang/test/CodeGen/call-graph-section.c
Original file line number Diff line number Diff line change
@@ -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, !"_ZTSFPiPcPfPdE.generalized"}
// MS-DAG: [[F_TPTR]] = !{i64 0, !"[email protected]"}

// CHECK-DAG: [[F_TSTRUCT_CT]] = !{[[F_TSTRUCT:![0-9]+]]}
// ITANIUM-DAG: [[F_TSTRUCT]] = !{i64 0, !"_ZTSFv3st2PS_E.generalized"}
// MS-DAG: [[F_TSTRUCT]] = !{i64 0, !"?6AXUst2@@PEAU0@@Z.generalized"}
Loading
Loading