Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[llvm] Introduce callee_type metadata #87573

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 31 commits into
base: users/Prabhuk/sprmain.clangcallgraphsection-add-type-id-metadata-to-indirect-call-and-targets
Choose a base branch
Loading
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3565040
[𝘀𝗽𝗿] initial version
necipfazil Apr 3, 2024
a8a5848
Update clang/lib/CodeGen/CodeGenModule.cpp
Prabhuk Apr 22, 2024
019b2ca
Update clang/lib/CodeGen/CodeGenModule.cpp
Prabhuk Apr 22, 2024
15d0727
Refactored calls to emit type id metadata to be within EmitCall funct…
Prabhuk Apr 24, 2024
feef77b
Rebased on top of main
Prabhuk Apr 24, 2024
9924290
dyn_cast to isa
Prabhuk Apr 29, 2024
76244d9
Addressed review comments and a FIXME. Rebased on upstream main.
Prabhuk May 1, 2024
179f930
Rebase patchset
necipfazil Nov 14, 2024
e31af7b
Update inline comment as suggested.
necipfazil Nov 14, 2024
a3ca3e2
Rebase on top of upstream main.
necipfazil Nov 20, 2024
9c94f5e
Break clang and llvm parts into separate commits.
Prabhuk Nov 20, 2024
24882b1
Address review comments. Break llvm and clang patches.
Prabhuk Dec 10, 2024
4041391
Address maybe unused comment
Prabhuk Feb 2, 2025
1942c70
Rebase on top of main.
Prabhuk Feb 2, 2025
995729b
Rename OB_type to OB_callee_type.
Prabhuk Feb 5, 2025
c202534
Rebase on top of main
Prabhuk Feb 11, 2025
10c0327
Address review comments on the test file.
Prabhuk Mar 19, 2025
27c970a
Remove callee_type operand bundle.
Prabhuk Apr 19, 2025
83c95a1
Handle instcombine usecase for callee_type metadata.
Prabhuk Apr 23, 2025
fdf6a1c
Verifier changes.
Prabhuk Apr 23, 2025
4b32ce1
Update the test.
Prabhuk Apr 23, 2025
11404d7
Add verifier test.
Prabhuk Apr 24, 2025
193551b
Add inliner test. Fix instcombine test.
Prabhuk Apr 28, 2025
3c99824
Update tests. Rebase on top of main.
Prabhuk May 1, 2025
018bc97
Remove direct call check from Verifier.
Prabhuk May 5, 2025
55c721b
Updated langref.
Prabhuk May 10, 2025
acdc10d
Update documentation.
Prabhuk May 14, 2025
bb3ae95
Fix doc typo.
Prabhuk May 14, 2025
4dee1b9
Address comments on changes to tests.
Prabhuk May 27, 2025
5a0f45f
Fix combine metadata and add tests.
Prabhuk Jun 11, 2025
c431e2e
Use smallptrset.
Prabhuk Jun 11, 2025
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
36 changes: 36 additions & 0 deletions 36 clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Type.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/CodeGen/CGFunctionInfo.h"
Expand Down Expand Up @@ -5021,6 +5022,11 @@ static unsigned getMaxVectorWidth(const llvm::Type *Ty) {
return MaxVectorWidth;
}

static bool isCXXDeclType(const FunctionDecl *FD) {
return isa<CXXConstructorDecl>(FD) || isa<CXXMethodDecl>(FD) ||
isa<CXXDestructorDecl>(FD);
}

RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
const CGCallee &Callee,
ReturnValueSlot ReturnValue,
Expand Down Expand Up @@ -5693,6 +5699,36 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
AllocAlignAttrEmitter AllocAlignAttrEmitter(*this, TargetDecl, CallArgs);
Attrs = AllocAlignAttrEmitter.TryEmitAsCallSiteAttribute(Attrs);

if (CGM.getCodeGenOpts().CallGraphSection) {
// Create operand bundle only for indirect calls, not for all
if (callOrInvoke && *callOrInvoke && (*callOrInvoke)->isIndirectCall()) {
Prabhuk marked this conversation as resolved.
Show resolved Hide resolved
assert((TargetDecl && TargetDecl->getFunctionType() ||
Callee.getAbstractInfo().getCalleeFunctionProtoType()) &&
"cannot find callsite type");
QualType CST;
if (TargetDecl && TargetDecl->getFunctionType())
CST = QualType(TargetDecl->getFunctionType(), 0);
else if (const auto *FPT =
Callee.getAbstractInfo().getCalleeFunctionProtoType())
CST = QualType(FPT, 0);

if (!CST.isNull()) {
auto *TypeIdMD = CGM.CreateMetadataIdentifierGeneralized(CST);
auto *TypeIdMDVal =
llvm::MetadataAsValue::get(getLLVMContext(), TypeIdMD);
BundleList.emplace_back("type", TypeIdMDVal);
}

// Set type identifier metadata of indirect calls for call graph section.
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(TargetDecl)) {
// Type id metadata is set only for C/C++ contexts.
if (isCXXDeclType(FD)) {
CGM.CreateFunctionTypeMetadataForIcall(FD->getType(), *callOrInvoke);
}
}
}
}

// Emit the actual call/invoke instruction.
llvm::CallBase *CI;
if (!InvokeDest) {
Expand Down
5 changes: 5 additions & 0 deletions 5 clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6013,6 +6013,11 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee
}
}

// Set type identifier metadata of indirect calls for call graph section.
if (CGM.getCodeGenOpts().CallGraphSection && CallOrInvoke &&
CallOrInvoke->isIndirectCall())
Prabhuk marked this conversation as resolved.
Show resolved Hide resolved
CGM.CreateFunctionTypeMetadataForIcall(QualType(FnType, 0), CallOrInvoke);

return Call;
}

Expand Down
35 changes: 30 additions & 5 deletions 35 clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2495,8 +2495,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.
Expand Down Expand Up @@ -2686,7 +2687,17 @@ static void setLinkageForGV(llvm::GlobalValue *GV, const NamedDecl *ND) {

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;

Expand All @@ -2697,14 +2708,27 @@ void CodeGenModule::CreateFunctionTypeMetadataForIcall(const FunctionDecl *FD,

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)
if (auto CrossDsoTypeId = CreateCrossDsoCfiTypeId(MD))
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::setKCFIType(const FunctionDecl *FD, llvm::Function *F) {
llvm::LLVMContext &Ctx = F->getContext();
llvm::MDBuilder MDB(Ctx);
Expand Down Expand Up @@ -2832,7 +2856,8 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F,
// 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 (LangOpts.Sanitize.has(SanitizerKind::KCFI))
Expand Down
4 changes: 4 additions & 0 deletions 4 clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1476,6 +1476,10 @@ class CodeGenModule : public CodeGenTypeCache {
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);

/// Set type metadata to the given function.
void setKCFIType(const FunctionDecl *FD, llvm::Function *F);

Expand Down
110 changes: 110 additions & 0 deletions 110 clang/test/CodeGen/call-graph-section-1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// 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 {{.*}} 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 callsite operand bundles)

// CST-LABEL: define {{.*}} @_Z3foov
void foo() {
Cls2 ObjCls2;
ObjCls2.fp = &Cls1::receiver;

// CST: call noundef ptr %{{.*}} [ "type"(metadata !"_ZTSFPvS_S_E.generalized") ]
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 %{{.*}} [ "type"(metadata !"_ZTSFicfdE.generalized") ]
(ObjCls2Ptr->*fp_f1)(0, 0, 0);

// CST: call noundef ptr %{{.*}} [ "type"(metadata !"_ZTSFPvS_S_S_E.generalized") ]
(ObjCls2Ptr->*fp_f2)(0, 0, 0);

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSFv4Cls1E.generalized") ]
(ObjCls2Ptr->*fp_f3)(Cls1Param);

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSFv4Cls1E.generalized") ]
(ObjCls2Ptr->*fp_f4)(Cls1Param);

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvPvE.generalized") ]
(ObjCls2Ptr->*fp_f5)(&Cls1Param);

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvPKvE.generalized") ]
(ObjCls2Ptr->*fp_f6)(&Cls1Param);

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvR4Cls1E.generalized") ]
(ObjCls2Ptr->*fp_f7)(Cls1Param);

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvRK4Cls1E.generalized") ]
(ObjCls2Ptr->*fp_f8)(Cls1Param);

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSKFvvE.generalized") ]
(ObjCls2Ptr->*fp_f9)();
}
95 changes: 95 additions & 0 deletions 95 clang/test/CodeGen/call-graph-section-2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// 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 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;

// 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>;
Cls1 Cls1Obj;

// CST: call noundef ptr %{{.*}} [ "type"(metadata !"_ZTSFPv4Cls1S_PKvRS0_RKS0_E.generalized") ]
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 %{{.*}} [ "type"(metadata !"_ZTSFvvE.generalized") ]
(Obj2Ptr->*fp_f1)();

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSFv4Cls1E.generalized") ]
(Obj2Ptr->*fp_f2)(Cls1Obj);

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvPvE.generalized") ]
(Obj2Ptr->*fp_f3)(&Cls1Obj);

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvPKvE.generalized") ]
(Obj2Ptr->*fp_f4)(&Cls1Obj);

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvR4Cls1E.generalized") ]
(Obj2Ptr->*fp_f5)(Cls1Obj);

// CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvRK4Cls1E.generalized") ]
(Obj2Ptr->*fp_f6)(Cls1Obj);
}
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.