Skip to content

Commit 550b62c

Browse files
authored
[clang] callee_type metadata for indirect calls (llvm#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
1 parent 48a99ad commit 550b62c

File tree

8 files changed

+516
-5
lines changed

8 files changed

+516
-5
lines changed

clang/lib/CodeGen/CGCall.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5937,8 +5937,24 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
59375937
CI->getCalledFunction()->getName().starts_with("_Z4sqrt")) {
59385938
SetSqrtFPAccuracy(CI);
59395939
}
5940-
if (callOrInvoke)
5940+
if (callOrInvoke) {
59415941
*callOrInvoke = CI;
5942+
if (CGM.getCodeGenOpts().CallGraphSection) {
5943+
QualType CST;
5944+
if (TargetDecl && TargetDecl->getFunctionType())
5945+
CST = QualType(TargetDecl->getFunctionType(), 0);
5946+
else if (const auto *FPT =
5947+
Callee.getAbstractInfo().getCalleeFunctionProtoType())
5948+
CST = QualType(FPT, 0);
5949+
else
5950+
llvm_unreachable(
5951+
"Cannot find the callee type to generate callee_type metadata.");
5952+
5953+
// Set type identifier metadata of indirect calls for call graph section.
5954+
if (!CST.isNull())
5955+
CGM.createCalleeTypeMetadataForIcall(CST, *callOrInvoke);
5956+
}
5957+
}
59425958

59435959
// If this is within a function that has the guard(nocf) attribute and is an
59445960
// indirect call, add the "guard_nocf" attribute to this call to indicate that

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2863,6 +2863,11 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
28632863
}
28642864
}
28652865

2866+
if (CodeGenOpts.CallGraphSection) {
2867+
if (auto *FD = dyn_cast<FunctionDecl>(D))
2868+
createIndirectFunctionTypeMD(FD, F);
2869+
}
2870+
28662871
// Emit type metadata on member functions for member function pointer checks.
28672872
// These are only ever necessary on definitions; we're guaranteed that the
28682873
// 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) {
30663071
GV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
30673072
}
30683073

3074+
static bool hasExistingGeneralizedTypeMD(llvm::Function *F) {
3075+
llvm::MDNode *MD = F->getMetadata(llvm::LLVMContext::MD_type);
3076+
return MD && MD->hasGeneralizedMDString();
3077+
}
3078+
3079+
void CodeGenModule::createIndirectFunctionTypeMD(const FunctionDecl *FD,
3080+
llvm::Function *F) {
3081+
// Return if generalized type metadata is already attached.
3082+
if (hasExistingGeneralizedTypeMD(F))
3083+
return;
3084+
3085+
// All functions which are not internal linkage could be indirect targets.
3086+
// Address taken functions with internal linkage could be indirect targets.
3087+
if (!F->hasLocalLinkage() ||
3088+
F->getFunction().hasAddressTaken(nullptr, /*IgnoreCallbackUses=*/true,
3089+
/*IgnoreAssumeLikeCalls=*/true,
3090+
/*IgnoreLLVMUsed=*/false))
3091+
F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType()));
3092+
}
3093+
30693094
void CodeGenModule::createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
30703095
llvm::Function *F) {
30713096
// Only if we are checking indirect calls.
@@ -3081,17 +3106,34 @@ void CodeGenModule::createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
30813106
/*GeneralizePointers=*/false);
30823107
llvm::Metadata *MD = CreateMetadataIdentifierForType(FnType);
30833108
F->addTypeMetadata(0, MD);
3084-
3085-
QualType GenPtrFnType = GeneralizeFunctionType(getContext(), FD->getType(),
3086-
/*GeneralizePointers=*/true);
3087-
F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(GenPtrFnType));
3109+
// Add the generalized identifier if not added already.
3110+
if (!hasExistingGeneralizedTypeMD(F)) {
3111+
QualType GenPtrFnType = GeneralizeFunctionType(getContext(), FD->getType(),
3112+
/*GeneralizePointers=*/true);
3113+
F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(GenPtrFnType));
3114+
}
30883115

30893116
// Emit a hash-based bit set entry for cross-DSO calls.
30903117
if (CodeGenOpts.SanitizeCfiCrossDso)
30913118
if (auto CrossDsoTypeId = CreateCrossDsoCfiTypeId(MD))
30923119
F->addTypeMetadata(0, llvm::ConstantAsMetadata::get(CrossDsoTypeId));
30933120
}
30943121

3122+
void CodeGenModule::createCalleeTypeMetadataForIcall(const QualType &QT,
3123+
llvm::CallBase *CB) {
3124+
// Only if needed for call graph section and only for indirect calls.
3125+
if (!CodeGenOpts.CallGraphSection || !CB->isIndirectCall())
3126+
return;
3127+
3128+
llvm::Metadata *TypeIdMD = CreateMetadataIdentifierGeneralized(QT);
3129+
llvm::MDTuple *TypeTuple = llvm::MDTuple::get(
3130+
getLLVMContext(), {llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(
3131+
llvm::Type::getInt64Ty(getLLVMContext()), 0)),
3132+
TypeIdMD});
3133+
llvm::MDTuple *MDN = llvm::MDNode::get(getLLVMContext(), {TypeTuple});
3134+
CB->setMetadata(llvm::LLVMContext::MD_callee_type, MDN);
3135+
}
3136+
30953137
void CodeGenModule::setKCFIType(const FunctionDecl *FD, llvm::Function *F) {
30963138
llvm::LLVMContext &Ctx = F->getContext();
30973139
llvm::MDBuilder MDB(Ctx);
@@ -3227,6 +3269,9 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F,
32273269
!CodeGenOpts.SanitizeCfiCanonicalJumpTables)
32283270
createFunctionTypeMetadataForIcall(FD, F);
32293271

3272+
if (CodeGenOpts.CallGraphSection)
3273+
createIndirectFunctionTypeMD(FD, F);
3274+
32303275
if (LangOpts.Sanitize.has(SanitizerKind::KCFI))
32313276
setKCFIType(FD, F);
32323277

clang/lib/CodeGen/CodeGenModule.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,6 +1644,13 @@ class CodeGenModule : public CodeGenTypeCache {
16441644
void createFunctionTypeMetadataForIcall(const FunctionDecl *FD,
16451645
llvm::Function *F);
16461646

1647+
/// Create and attach type metadata if the function is a potential indirect
1648+
/// call target to support call graph section.
1649+
void createIndirectFunctionTypeMD(const FunctionDecl *FD, llvm::Function *F);
1650+
1651+
/// Create and attach type metadata to the given call.
1652+
void createCalleeTypeMetadataForIcall(const QualType &QT, llvm::CallBase *CB);
1653+
16471654
/// Set type metadata to the given function.
16481655
void setKCFIType(const FunctionDecl *FD, llvm::Function *F);
16491656

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Tests that callback function whose address is taken is attached Type ID metadata
2+
// as it is a potential indirect call target.
3+
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
5+
// RUN: -emit-llvm -o %t %s
6+
// RUN: FileCheck %s < %t
7+
8+
////////////////////////////////////////////////////////////////////////////////
9+
typedef void (*CallbackFn)(int);
10+
11+
// Callback function with "internal" linkage.
12+
// CHECK-LABEL: define internal void @_ZL10myCallbacki(
13+
// CHECK-SAME: {{.*}} !type [[F_CALLBACK:![0-9]+]]
14+
static void myCallback(int value)
15+
{
16+
volatile int sink = value;
17+
(void)sink;
18+
}
19+
20+
int takeCallbackAddress() {
21+
// Take the address of the callback explicitly (address-taken function)
22+
CallbackFn cb = &myCallback;
23+
// Store the address in a volatile pointer to keep it observable
24+
volatile void* addr = (void*)cb;
25+
(void)addr;
26+
27+
return 0;
28+
}
29+
30+
// CHECK: [[F_CALLBACK]] = !{i64 0, !"_ZTSFviE.generalized"}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Tests that we assign appropriate identifiers to indirect calls and targets
2+
// specifically for C++ templates.
3+
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
5+
// RUN: -emit-llvm -o %t %s
6+
// RUN: FileCheck --check-prefix=FT %s < %t
7+
// RUN: FileCheck --check-prefix=CST %s < %t
8+
9+
////////////////////////////////////////////////////////////////////////////////
10+
// Class definitions and template classes (check for indirect target metadata)
11+
12+
class Cls1 {};
13+
14+
// Cls2 is instantiated with T=Cls1 in foo(). Following checks are for this
15+
// instantiation.
16+
template <class T>
17+
class Cls2 {
18+
public:
19+
// FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f1Ev(
20+
// FT-SAME: {{.*}} !type [[F_TCLS2F1:![0-9]+]]
21+
void f1() {}
22+
23+
// FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f2ES0_(
24+
// FT-SAME: {{.*}} !type [[F_TCLS2F2:![0-9]+]]
25+
void f2(T a) {}
26+
27+
// FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f3EPS0_(
28+
// FT-SAME: {{.*}} !type [[F_TCLS2F3:![0-9]+]]
29+
void f3(T *a) {}
30+
31+
// FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f4EPKS0_(
32+
// FT-SAME: {{.*}} !type [[F_TCLS2F4:![0-9]+]]
33+
void f4(const T *a) {}
34+
35+
// FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f5ERS0_(
36+
// FT-SAME: {{.*}} !type [[F_TCLS2F5:![0-9]+]]
37+
void f5(T &a) {}
38+
39+
// FT-LABEL: define {{.*}} void @_ZN4Cls2I4Cls1E2f6ERKS0_(
40+
// FT-SAME: {{.*}} !type [[F_TCLS2F6:![0-9]+]]
41+
void f6(const T &a) {}
42+
43+
// Mixed type function pointer member
44+
T *(*fp)(T a, T *b, const T *c, T &d, const T &e);
45+
};
46+
47+
// FT: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFvvE.generalized"}
48+
// FT: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"}
49+
// FT: [[F_TCLS2F3]] = !{i64 0, !"_ZTSFvP4Cls1E.generalized"}
50+
// FT: [[F_TCLS2F4]] = !{i64 0, !"_ZTSFvPK4Cls1E.generalized"}
51+
// FT: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"}
52+
// FT: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"}
53+
54+
////////////////////////////////////////////////////////////////////////////////
55+
// Callsites (check for indirect callsite operand bundles)
56+
57+
template <class T>
58+
T *T_func(T a, T *b, const T *c, T &d, const T &e) { return b; }
59+
60+
// CST-LABEL: define {{.*}} @_Z3foov
61+
// CST-SAME: {{.*}} !type [[F_TCLS2F1:![0-9]+]]
62+
void foo() {
63+
// Methods for Cls2<Cls1> is checked above within the template description.
64+
Cls2<Cls1> Obj;
65+
66+
Obj.fp = T_func<Cls1>;
67+
Cls1 Cls1Obj;
68+
69+
// CST: call noundef ptr %{{.*}}, !callee_type [[F_TFUNC_CLS1_CT:![0-9]+]]
70+
Obj.fp(Cls1Obj, &Cls1Obj, &Cls1Obj, Cls1Obj, Cls1Obj);
71+
72+
// Make indirect calls to Cls2's member methods
73+
auto fp_f1 = &Cls2<Cls1>::f1;
74+
auto fp_f2 = &Cls2<Cls1>::f2;
75+
auto fp_f3 = &Cls2<Cls1>::f3;
76+
auto fp_f4 = &Cls2<Cls1>::f4;
77+
auto fp_f5 = &Cls2<Cls1>::f5;
78+
auto fp_f6 = &Cls2<Cls1>::f6;
79+
80+
auto *Obj2Ptr = &Obj;
81+
82+
// CST: call void %{{.*}}, !callee_type [[F_TCLS2F1_CT:![0-9]+]]
83+
(Obj2Ptr->*fp_f1)();
84+
85+
// CST: call void %{{.*}}, !callee_type [[F_TCLS2F2_CT:![0-9]+]]
86+
(Obj2Ptr->*fp_f2)(Cls1Obj);
87+
88+
// CST: call void %{{.*}}, !callee_type [[F_TCLS2F3_CT:![0-9]+]]
89+
(Obj2Ptr->*fp_f3)(&Cls1Obj);
90+
91+
// CST: call void %{{.*}}, !callee_type [[F_TCLS2F4_CT:![0-9]+]]
92+
(Obj2Ptr->*fp_f4)(&Cls1Obj);
93+
94+
// CST: call void %{{.*}}, !callee_type [[F_TCLS2F5_CT:![0-9]+]]
95+
(Obj2Ptr->*fp_f5)(Cls1Obj);
96+
97+
// CST: call void %{{.*}}, !callee_type [[F_TCLS2F6_CT:![0-9]+]]
98+
(Obj2Ptr->*fp_f6)(Cls1Obj);
99+
}
100+
101+
// CST-LABEL: define {{.*}} @_Z6T_funcI4Cls1EPT_S1_S2_PKS1_RS1_RS3_(
102+
// CST-SAME: {{.*}} !type [[F_TFUNC_CLS1:![0-9]+]]
103+
104+
// CST: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFvvE.generalized"}
105+
// CST: [[F_TFUNC_CLS1_CT]] = !{[[F_TFUNC_CLS1:![0-9]+]]}
106+
// CST: [[F_TFUNC_CLS1]] = !{i64 0, !"_ZTSFP4Cls1S_S0_PKS_RS_RS1_E.generalized"}
107+
// CST: [[F_TCLS2F1_CT]] = !{[[F_TCLS2F1:![0-9]+]]}
108+
// CST: [[F_TCLS2F2_CT]] = !{[[F_TCLS2F2:![0-9]+]]}
109+
// CST: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"}
110+
// CST: [[F_TCLS2F3_CT]] = !{[[F_TCLS2F3:![0-9]+]]}
111+
// CST: [[F_TCLS2F3]] = !{i64 0, !"_ZTSFvP4Cls1E.generalized"}
112+
// CST: [[F_TCLS2F4_CT]] = !{[[F_TCLS2F4:![0-9]+]]}
113+
// CST: [[F_TCLS2F4]] = !{i64 0, !"_ZTSFvPK4Cls1E.generalized"}
114+
// CST: [[F_TCLS2F5_CT]] = !{[[F_TCLS2F5:![0-9]+]]}
115+
// CST: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"}
116+
// CST: [[F_TCLS2F6_CT]] = !{[[F_TCLS2F6:![0-9]+]]}
117+
// CST: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Tests that we assign appropriate identifiers to indirect calls and targets
2+
// specifically for virtual methods.
3+
4+
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fexperimental-call-graph-section \
5+
// RUN: -emit-llvm -o %t %s
6+
// RUN: FileCheck --check-prefix=FT %s < %t
7+
// RUN: FileCheck --check-prefix=CST %s < %t
8+
9+
////////////////////////////////////////////////////////////////////////////////
10+
// Class definitions (check for indirect target metadata)
11+
12+
class Base {
13+
public:
14+
// FT-LABEL: define {{.*}} @_ZN4Base2vfEPc(
15+
// FT-SAME: {{.*}} !type [[F_TVF:![0-9]+]]
16+
virtual int vf(char *a) { return 0; };
17+
};
18+
19+
class Derived : public Base {
20+
public:
21+
// FT: define {{.*}} @_ZN7Derived2vfEPc({{.*}} !type [[F_TVF]]
22+
int vf(char *a) override { return 1; };
23+
};
24+
25+
// FT: [[F_TVF]] = !{i64 0, !"_ZTSFiPcE.generalized"}
26+
27+
////////////////////////////////////////////////////////////////////////////////
28+
// Callsites (check for indirect callsite operand bundles)
29+
30+
// CST-LABEL: define {{.*}} @_Z3foov
31+
void foo() {
32+
auto B = Base();
33+
auto D = Derived();
34+
35+
Base *Bptr = &B;
36+
Base *BptrToD = &D;
37+
Derived *Dptr = &D;
38+
39+
auto FpBaseVf = &Base::vf;
40+
auto FpDerivedVf = &Derived::vf;
41+
42+
// CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
43+
(Bptr->*FpBaseVf)(0);
44+
45+
// CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
46+
(BptrToD->*FpBaseVf)(0);
47+
48+
// CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
49+
(Dptr->*FpBaseVf)(0);
50+
51+
// CST: call noundef i32 %{{.*}}, !callee_type [[F_TVF_CT:![0-9]+]]
52+
(Dptr->*FpDerivedVf)(0);
53+
}
54+
55+
// CST: [[F_TVF_CT]] = !{[[F_TVF:![0-9]+]]}
56+
// CST: [[F_TVF]] = !{i64 0, !"_ZTSFiPcE.generalized"}

0 commit comments

Comments
 (0)