35 changes: 30 additions & 5 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2622,8 +2622,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 @@ -2813,7 +2814,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 @@ -2824,14 +2835,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 @@ -2959,7 +2983,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 clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,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
4 changes: 0 additions & 4 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6609,10 +6609,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back(A->getValue());
}

if (Args.hasFlag(options::OPT_fcall_graph_section,
options::OPT_fno_call_graph_section, false))
CmdArgs.push_back("-fcall-graph-section");

Args.addOptInFlag(CmdArgs, options::OPT_fstack_size_section,
options::OPT_fno_stack_size_section);

Expand Down
110 changes: 110 additions & 0 deletions 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 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);
}
52 changes: 52 additions & 0 deletions clang/test/CodeGen/call-graph-section-3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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 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 %{{.*}} [ "type"(metadata !"_ZTSFiPvE.generalized") ]
(Bptr->*FpBaseVf)(0);

// CST: call noundef i32 %{{.*}} [ "type"(metadata !"_ZTSFiPvE.generalized") ]
(BptrToD->*FpBaseVf)(0);

// CST: call noundef i32 %{{.*}} [ "type"(metadata !"_ZTSFiPvE.generalized") ]
(Dptr->*FpBaseVf)(0);

// CST: call noundef i32 %{{.*}} [ "type"(metadata !"_ZTSFiPvE.generalized") ]
(Dptr->*FpDerivedVf)(0);
}
85 changes: 85 additions & 0 deletions clang/test/CodeGen/call-graph-section.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// 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;
// ITANIUM: call {{.*}} [ "type"(metadata !"_ZTSFvE.generalized") ]
// MS: call {{.*}} [ "type"(metadata !"?6AX@Z.generalized") ]
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;
// ITANIUM: call i32 {{.*}} [ "type"(metadata !"_ZTSFicfdE.generalized") ]
// MS: call i32 {{.*}} [ "type"(metadata !"?6AHDMN@Z.generalized") ]
fp_baz('a', .0f, .0);

int *(*fp_qux)(char *, float *, double *) = qux;
// ITANIUM: call ptr {{.*}} [ "type"(metadata !"_ZTSFPvS_S_S_E.generalized") ]
// MS: call ptr {{.*}} [ "type"(metadata !"?6APEAXPEAX00@Z.generalized") ]
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;
// ITANIUM: call ptr {{.*}} [ "type"(metadata !"_ZTSFPvS_S_S_E.generalized") ]
// MS: call ptr {{.*}} [ "type"(metadata !"?6APEAXPEAX00@Z.generalized") ]
St1.fp(0, 0, 0);

struct st2 St2;
St2.m.fp = qux;
// ITANIUM: call ptr {{.*}} [ "type"(metadata !"_ZTSFPvS_S_S_E.generalized") ]
// MS: call ptr {{.*}} [ "type"(metadata !"?6APEAXPEAX00@Z.generalized") ]
St2.m.fp(0, 0, 0);

// ITANIUM: call void {{.*}} [ "type"(metadata !"_ZTSFv3st2PvE.generalized") ]
// MS: call void {{.*}} [ "type"(metadata !"?6AXUst2@@PEAX@Z.generalized") ]
void (*fp_stparam)(struct st2, struct st2 *) = stparam;
fp_stparam(St2, &St2);
}

// ITANIUM-DAG: [[F_TVOID]] = !{i64 0, !"_ZTSFvE.generalized"}
// MS-DAG: [[F_TVOID]] = !{i64 0, !"?6AX@Z.generalized"}

// ITANIUM-DAG: [[F_TPRIMITIVE]] = !{i64 0, !"_ZTSFicfdE.generalized"}
// MS-DAG: [[F_TPRIMITIVE]] = !{i64 0, !"?6AHDMN@Z.generalized"}

// ITANIUM-DAG: [[F_TPTR]] = !{i64 0, !"_ZTSFPvS_S_S_E.generalized"}
// MS-DAG: [[F_TPTR]] = !{i64 0, !"?6APEAXPEAX00@Z.generalized"}

// ITANIUM-DAG: [[F_TSTRUCT]] = !{i64 0, !"_ZTSFv3st2PvE.generalized"}
// MS-DAG: [[F_TSTRUCT]] = !{i64 0, !"?6AXUst2@@PEAX@Z.generalized"}
5 changes: 0 additions & 5 deletions clang/test/Driver/call-graph-section.c

This file was deleted.

28 changes: 28 additions & 0 deletions llvm/include/llvm/CodeGen/AsmPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,32 @@ class AsmPrinter : public MachineFunctionPass {
/// Emit comments in assembly output if this is true.
bool VerboseAsm;

/// Store symbols and type identifiers used to create call graph section
/// entries related to a function.
struct FunctionInfo {
/// Numeric type identifier used in call graph section for indirect calls
/// and targets.
using CGTypeId = uint64_t;

/// Enumeration of function kinds, and their mapping to function kind values
/// stored in call graph section entries.
/// Must match the enum in llvm/tools/llvm-objdump/llvm-objdump.cpp.
enum FunctionKind {
/// Function cannot be target to indirect calls.
NOT_INDIRECT_TARGET = 0,

/// Function may be target to indirect calls but its type id is unknown.
INDIRECT_TARGET_UNKNOWN_TID = 1,

/// Function may be target to indirect calls and its type id is known.
INDIRECT_TARGET_KNOWN_TID = 2,
};

/// Map type identifiers to callsite labels. Labels are only for indirect
/// calls and inclusive of all indirect calls of the function.
SmallVector<std::pair<CGTypeId, MCSymbol *>> CallSiteLabels;
};

/// Output stream for the stack usage file (i.e., .su file).
std::unique_ptr<raw_fd_ostream> StackUsageStream;

Expand Down Expand Up @@ -414,6 +440,8 @@ class AsmPrinter : public MachineFunctionPass {
void emitKCFITrapEntry(const MachineFunction &MF, const MCSymbol *Symbol);
virtual void emitKCFITypeId(const MachineFunction &MF);

void emitCallGraphSection(const MachineFunction &MF, FunctionInfo &FuncInfo);

void emitPseudoProbe(const MachineInstr &MI);

void emitRemarksSection(remarks::RemarkStreamer &RS);
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/CodeGen/MIRYamlMapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,10 @@ struct CallSiteInfo {
MachineInstrLoc CallLocation;
std::vector<ArgRegPair> ArgForwardingRegs;

/// Numeric callee type identifier used for call graph section.
using TypeIdTy = std::optional<uint64_t>;
TypeIdTy TypeId;

bool operator==(const CallSiteInfo &Other) const {
return CallLocation.BlockNum == Other.CallLocation.BlockNum &&
CallLocation.Offset == Other.CallLocation.Offset;
Expand Down Expand Up @@ -511,6 +515,7 @@ template <> struct MappingTraits<CallSiteInfo> {
YamlIO.mapRequired("offset", CSInfo.CallLocation.Offset);
YamlIO.mapOptional("fwdArgRegs", CSInfo.ArgForwardingRegs,
std::vector<CallSiteInfo::ArgRegPair>());
YamlIO.mapOptional("typeId", CSInfo.TypeId);
}

static const bool flow = true;
Expand Down
41 changes: 39 additions & 2 deletions llvm/include/llvm/CodeGen/MachineFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/EHPersonalities.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/ArrayRecycler.h"
#include "llvm/Support/AtomicOrdering.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/Recycler.h"
#include "llvm/Target/TargetOptions.h"
#include <bitset>
Expand Down Expand Up @@ -486,14 +489,48 @@ class LLVM_ABI MachineFunction {
struct CallSiteInfo {
/// Vector of call argument and its forwarding register.
SmallVector<ArgRegPair, 1> ArgRegPairs;

/// Callee type id.
ConstantInt *TypeId = nullptr;

CallSiteInfo() = default;

/// Extracts the numeric type id from the CallBase's type operand bundle,
/// and sets TypeId. This is used as type id for the indirect call in the
/// call graph section.
CallSiteInfo(const CallBase &CB) {
// Call graph section needs numeric type id only for indirect calls.
if (!CB.isIndirectCall())
return;

std::optional<OperandBundleUse> Opt =
CB.getOperandBundle(LLVMContext::OB_type);
// Return if the operand bundle for call graph section cannot be found.
if (!Opt)
return;

// Get generalized type id string
auto OB = *Opt;
assert(OB.Inputs.size() == 1 && "invalid input size");
auto *OBVal = OB.Inputs.front().get();
auto *TypeIdMD = cast<MetadataAsValue>(OBVal)->getMetadata();
auto *TypeIdStr = cast<MDString>(TypeIdMD);
assert(TypeIdStr->getString().endswith(".generalized") &&
"invalid type identifier");

// Compute numeric type id from generalized type id string
uint64_t TypeIdVal = MD5Hash(TypeIdStr->getString());
IntegerType *Int64Ty = Type::getInt64Ty(CB.getContext());
TypeId = ConstantInt::get(Int64Ty, TypeIdVal, /*IsSigned=*/false);
}
};

private:
Delegate *TheDelegate = nullptr;
GISelChangeObserver *Observer = nullptr;

using CallSiteInfoMap = DenseMap<const MachineInstr *, CallSiteInfo>;
/// Map a call instruction to call site arguments forwarding info.
/// Map a call instruction to call site arguments forwarding and type id.
CallSiteInfoMap CallSitesInfo;

/// A helper function that returns call site info for a give call
Expand Down Expand Up @@ -1355,7 +1392,7 @@ class LLVM_ABI MachineFunction {
});
}

/// Start tracking the arguments passed to the call \p CallI.
/// Start tracking the arguments passed to the call \p CallI and call type.
void addCallSiteInfo(const MachineInstr *CallI, CallSiteInfo &&CallInfo) {
assert(CallI->isCandidateForCallSiteEntry());
bool Inserted =
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/LLVMContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class LLVMContext {
OB_ptrauth = 7, // "ptrauth"
OB_kcfi = 8, // "kcfi"
OB_convergencectrl = 9, // "convergencectrl"
OB_type = 10, // "type"
};

/// getMDKindID - Return a unique non-zero ID for the specified metadata kind.
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/MC/MCObjectFileInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ class MCObjectFileInfo {
/// Language Specific Data Area information is emitted to.
MCSection *LSDASection = nullptr;

/// Section containing metadata on call graph.
MCSection *CallGraphSection = nullptr;

/// If exception handling is supported by the target and the target can
/// support a compact representation of the CIE and FDE, this is the section
/// to emit them into.
Expand Down Expand Up @@ -355,6 +358,8 @@ class MCObjectFileInfo {
MCSection *getFaultMapSection() const { return FaultMapSection; }
MCSection *getRemarksSection() const { return RemarksSection; }

MCSection *getCallGraphSection(const MCSection &TextSec) const;

MCSection *getStackSizesSection(const MCSection &TextSec) const;

MCSection *getBBAddrMapSection(const MCSection &TextSec) const;
Expand Down
9 changes: 5 additions & 4 deletions llvm/include/llvm/Target/TargetOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,11 @@ namespace llvm {
EmulatedTLS(false), EnableTLSDESC(false), EnableIPRA(false),
EmitStackSizeSection(false), EnableMachineOutliner(false),
EnableMachineFunctionSplitter(false), SupportsDefaultOutlining(false),
EmitAddrsig(false), BBAddrMap(false), EmitCallGraphSection(false), EmitCallSiteInfo(false),
SupportsDebugEntryValues(false), EnableDebugEntryValues(false),
ValueTrackingVariableLocations(false), ForceDwarfFrameSection(false),
XRayFunctionIndex(true), DebugStrictDwarf(false), Hotpatch(false),
EmitAddrsig(false), BBAddrMap(false), EmitCallGraphSection(false),
EmitCallSiteInfo(false), SupportsDebugEntryValues(false),
EnableDebugEntryValues(false), ValueTrackingVariableLocations(false),
ForceDwarfFrameSection(false), XRayFunctionIndex(true),
DebugStrictDwarf(false), Hotpatch(false),
PPCGenScalarMASSEntries(false), JMCInstrument(false),
EnableCFIFixup(false), MisExpect(false), XCOFFReadOnlyPointers(false),
VerifyArgABICompliance(true),
Expand Down
125 changes: 125 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1629,6 +1629,105 @@ void AsmPrinter::emitStackUsage(const MachineFunction &MF) {
*StackUsageStream << "static\n";
}

/// Extracts a generalized numeric type identifier of a Function's type from
/// type metadata. Returns null if metadata cannot be found.
static ConstantInt *extractNumericCGTypeId(const Function &F) {
SmallVector<MDNode *, 2> Types;
F.getMetadata(LLVMContext::MD_type, Types);
MDString *MDGeneralizedTypeId = nullptr;
for (const auto &Type : Types) {
if (Type->getNumOperands() == 2 && isa<MDString>(Type->getOperand(1))) {
auto *TMDS = cast<MDString>(Type->getOperand(1));
if (TMDS->getString().ends_with("generalized")) {
MDGeneralizedTypeId = TMDS;
break;
}
}
}

if (!MDGeneralizedTypeId) {
errs() << "warning: can't find indirect target type id metadata "
<< "for " << F.getName() << "\n";
return nullptr;
}

uint64_t TypeIdVal = llvm::MD5Hash(MDGeneralizedTypeId->getString());
Type *Int64Ty = Type::getInt64Ty(F.getContext());
return cast<ConstantInt>(ConstantInt::get(Int64Ty, TypeIdVal));
}

/// Emits call graph section.
void AsmPrinter::emitCallGraphSection(const MachineFunction &MF,
FunctionInfo &FuncInfo) {
if (!MF.getTarget().Options.EmitCallGraphSection)
return;

// Switch to the call graph section for the function
MCSection *FuncCGSection =
getObjFileLowering().getCallGraphSection(*getCurrentSection());
assert(FuncCGSection && "null call graph section");
OutStreamer->pushSection();
OutStreamer->switchSection(FuncCGSection);

// Emit format version number.
OutStreamer->emitInt64(0);

// Emit function's self information, which is composed of:
// 1) FunctionEntryPc
// 2) FunctionKind: Whether the function is indirect target, and if so,
// whether its type id is known.
// 3) FunctionTypeId: Emit only when the function is an indirect target
// and its type id is known.

// Emit function entry pc.
const MCSymbol *FunctionSymbol = getFunctionBegin();
OutStreamer->emitSymbolValue(FunctionSymbol, TM.getProgramPointerSize());

// If this function has external linkage or has its address taken and
// it is not a callback, then anything could call it.
const Function &F = MF.getFunction();
bool IsIndirectTarget =
!F.hasLocalLinkage() || F.hasAddressTaken(nullptr,
/*IgnoreCallbackUses=*/true,
/*IgnoreAssumeLikeCalls=*/true,
/*IgnoreLLVMUsed=*/false);

// FIXME: FunctionKind takes a few values but emitted as a 64-bit value.
// Can be optimized to occupy 2 bits instead.
// Emit function kind, and type id if available.
if (!IsIndirectTarget) {
OutStreamer->emitInt64(FunctionInfo::FunctionKind::NOT_INDIRECT_TARGET);
} else {
const auto *TypeId = extractNumericCGTypeId(F);
if (TypeId) {
OutStreamer->emitInt64(
FunctionInfo::FunctionKind::INDIRECT_TARGET_KNOWN_TID);
OutStreamer->emitInt64(TypeId->getZExtValue());
} else {
OutStreamer->emitInt64(
FunctionInfo::FunctionKind::INDIRECT_TARGET_UNKNOWN_TID);
}
}

// Emit callsite labels, where each element is a pair of type id and
// indirect callsite pc.
const auto &CallSiteLabels = FuncInfo.CallSiteLabels;

// Emit the count of pairs.
OutStreamer->emitInt64(CallSiteLabels.size());

// Emit the type id and call site label pairs.
for (const std::pair<uint64_t, MCSymbol *> &El : CallSiteLabels) {
auto TypeId = El.first;
const auto &Label = El.second;
OutStreamer->emitInt64(TypeId);
OutStreamer->emitSymbolValue(Label, TM.getProgramPointerSize());
}
FuncInfo.CallSiteLabels.clear();

OutStreamer->popSection();
}

void AsmPrinter::emitPCSectionsLabel(const MachineFunction &MF,
const MDNode &MD) {
MCSymbol *S = MF.getContext().createTempSymbol("pcsection");
Expand Down Expand Up @@ -1805,6 +1904,8 @@ void AsmPrinter::emitFunctionBody() {
MBBSectionRanges[MF->front().getSectionID()] =
MBBSectionRange{CurrentFnBegin, nullptr};

FunctionInfo FuncInfo;
const auto &CallSitesInfoMap = MF->getCallSitesInfo();
for (auto &MBB : *MF) {
// Print a label for the basic block.
emitBasicBlockStart(MBB);
Expand Down Expand Up @@ -1935,6 +2036,26 @@ void AsmPrinter::emitFunctionBody() {
break;
}

// FIXME: Some indirect calls can get lowered to jump instructions,
// resulting in emitting labels for them. The extra information can
// be neglected while disassembling but still takes space in the binary.
if (TM.Options.EmitCallGraphSection && MI.isCall()) {
// Only indirect calls have type identifiers set.
const auto &CallSiteInfo = CallSitesInfoMap.find(&MI);
if (CallSiteInfo != CallSitesInfoMap.end()) {
if (auto *TypeId = CallSiteInfo->second.TypeId) {
// Emit label.
MCSymbol *S = MF->getContext().createTempSymbol();
OutStreamer->emitLabel(S);

// Get type id value.
uint64_t TypeIdVal = TypeId->getZExtValue();

// Add to function's callsite labels.
FuncInfo.CallSiteLabels.emplace_back(TypeIdVal, S);
}
}
}
// If there is a post-instruction symbol, emit a label for it here.
if (MCSymbol *S = MI.getPostInstrSymbol())
OutStreamer->emitLabel(S);
Expand Down Expand Up @@ -2112,6 +2233,9 @@ void AsmPrinter::emitFunctionBody() {
// Emit section containing stack size metadata.
emitStackSizeSection(*MF);

// Emit section containing call graph metadata.
emitCallGraphSection(*MF, FuncInfo);

// Emit .su file containing function stack size information.
emitStackUsage(*MF);

Expand Down Expand Up @@ -2697,6 +2821,7 @@ void AsmPrinter::SetupMachineFunction(MachineFunction &MF) {
F.hasFnAttribute("xray-instruction-threshold") ||
needFuncLabels(MF, *this) || NeedsLocalForSize ||
MF.getTarget().Options.EmitStackSizeSection ||
MF.getTarget().Options.EmitCallGraphSection ||
MF.getTarget().Options.BBAddrMap) {
CurrentFnBegin = createTempSymbol("func_begin");
if (NeedsLocalForSize)
Expand Down
10 changes: 8 additions & 2 deletions llvm/lib/CodeGen/MIRParser/MIRParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,12 +488,18 @@ bool MIRParserImpl::initializeCallSiteInfo(
return error(Error, ArgRegPair.Reg.SourceRange);
CSInfo.ArgRegPairs.emplace_back(Reg, ArgRegPair.ArgNo);
}
if (YamlCSInfo.TypeId.has_value()) {
IntegerType *Int64Ty = Type::getInt64Ty(Context);
CSInfo.TypeId = ConstantInt::get(Int64Ty, YamlCSInfo.TypeId.value(),
/*isSigned=*/false);
}

if (TM.Options.EmitCallSiteInfo)
if (TM.Options.EmitCallSiteInfo || TM.Options.EmitCallGraphSection)
MF.addCallSiteInfo(&*CallI, std::move(CSInfo));
}

if (YamlMF.CallSitesInfo.size() && !TM.Options.EmitCallSiteInfo)
if (YamlMF.CallSitesInfo.size() &&
!(TM.Options.EmitCallSiteInfo || TM.Options.EmitCallGraphSection))
return error(Twine("Call site info provided but not used"));
return false;
}
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/CodeGen/MIRPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,9 @@ void MIRPrinter::convertCallSiteObjects(yaml::MachineFunction &YMF,
printRegMIR(ArgReg.Reg, YmlArgReg.Reg, TRI);
YmlCS.ArgForwardingRegs.emplace_back(YmlArgReg);
}
// Get type id.
if (CSInfo.second.TypeId)
YmlCS.TypeId = CSInfo.second.TypeId->getZExtValue();
YMF.CallSitesInfo.push_back(YmlCS);
}

Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/CodeGen/MachineFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ MachineFunction::getCallSiteInfo(const MachineInstr *MI) {
assert(MI->isCandidateForCallSiteEntry() &&
"Call site info refers only to call (MI) candidates");

if (!Target.Options.EmitCallSiteInfo)
if (!Target.Options.EmitCallSiteInfo && !Target.Options.EmitCallGraphSection)
return CallSitesInfo.end();
return CallSitesInfo.find(MI);
}
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,8 @@ EmitSchedule(MachineBasicBlock::iterator &InsertPos) {
}

if (MI->isCandidateForCallSiteEntry() &&
DAG->getTarget().Options.EmitCallSiteInfo) {
(DAG->getTarget().Options.EmitCallSiteInfo ||
DAG->getTarget().Options.EmitCallGraphSection)) {
MF.addCallSiteInfo(MI, DAG->getCallSiteInfo(Node));
}

Expand Down
9 changes: 5 additions & 4 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3326,7 +3326,7 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
{LLVMContext::OB_deopt, LLVMContext::OB_gc_transition,
LLVMContext::OB_gc_live, LLVMContext::OB_funclet,
LLVMContext::OB_cfguardtarget, LLVMContext::OB_ptrauth,
LLVMContext::OB_clang_arc_attachedcall}) &&
LLVMContext::OB_clang_arc_attachedcall, LLVMContext::OB_type}) &&
"Cannot lower invokes with arbitrary operand bundles yet!");

const Value *Callee(I.getCalledOperand());
Expand Down Expand Up @@ -3415,8 +3415,9 @@ void SelectionDAGBuilder::visitCallBr(const CallBrInst &I) {

// Deopt bundles are lowered in LowerCallSiteWithDeoptBundle, and we don't
// have to do anything here to lower funclet bundles.
assert(!I.hasOperandBundlesOtherThan(
{LLVMContext::OB_deopt, LLVMContext::OB_funclet}) &&
assert(!I.hasOperandBundlesOtherThan({LLVMContext::OB_deopt,
LLVMContext::OB_funclet,
LLVMContext::OB_type}) &&
"Cannot lower callbrs with arbitrary operand bundles yet!");

assert(I.isInlineAsm() && "Only know how to handle inlineasm callbr");
Expand Down Expand Up @@ -9599,7 +9600,7 @@ void SelectionDAGBuilder::visitCall(const CallInst &I) {
{LLVMContext::OB_deopt, LLVMContext::OB_funclet,
LLVMContext::OB_cfguardtarget, LLVMContext::OB_preallocated,
LLVMContext::OB_clang_arc_attachedcall, LLVMContext::OB_kcfi,
LLVMContext::OB_convergencectrl}) &&
LLVMContext::OB_convergencectrl, LLVMContext::OB_type}) &&
"Cannot lower calls with arbitrary operand bundles!");

SDValue Callee = getValue(I.getCalledOperand());
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/IR/LLVMContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ LLVMContext::LLVMContext() : pImpl(new LLVMContextImpl(*this)) {
"convergencectrl operand bundle id drifted!");
(void)ConvergenceCtrlEntry;

auto *TypeEntry = pImpl->getOrInsertBundleTag("type");
assert(TypeEntry->second == LLVMContext::OB_type &&
"type operand bundle id drifted!");
(void)TypeEntry;

SyncScope::ID SingleThreadSSID =
pImpl->getOrInsertSyncScopeID("singlethread");
assert(SingleThreadSSID == SyncScope::SingleThread &&
Expand Down
10 changes: 7 additions & 3 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3708,13 +3708,14 @@ void Verifier::visitCallBase(CallBase &Call) {
visitIntrinsicCall(ID, Call);

// Verify that a callsite has at most one "deopt", at most one "funclet", at
// most one "gc-transition", at most one "cfguardtarget", at most one
// "preallocated" operand bundle, and at most one "ptrauth" operand bundle.
// most one "gc-transition", at most one "cfguardtarget", at most one "type",
// at most one "preallocated" operand bundle, and at most one "ptrauth"
// operand bundle.
bool FoundDeoptBundle = false, FoundFuncletBundle = false,
FoundGCTransitionBundle = false, FoundCFGuardTargetBundle = false,
FoundPreallocatedBundle = false, FoundGCLiveBundle = false,
FoundPtrauthBundle = false, FoundKCFIBundle = false,
FoundAttachedCallBundle = false;
FoundAttachedCallBundle = false, FoundTypeBundle = false;
for (unsigned i = 0, e = Call.getNumOperandBundles(); i < e; ++i) {
OperandBundleUse BU = Call.getOperandBundleAt(i);
uint32_t Tag = BU.getTagID();
Expand Down Expand Up @@ -3777,6 +3778,9 @@ void Verifier::visitCallBase(CallBase &Call) {
"Multiple \"clang.arc.attachedcall\" operand bundles", Call);
FoundAttachedCallBundle = true;
verifyAttachedCallBundle(Call, BU);
} else if (Tag == LLVMContext::OB_type) {
Check(!FoundTypeBundle, "Multiple \"type\" operand bundles", Call);
FoundTypeBundle = true;
}
}

Expand Down
20 changes: 20 additions & 0 deletions llvm/lib/MC/MCObjectFileInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,8 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) {
EHFrameSection =
Ctx->getELFSection(".eh_frame", EHSectionType, EHSectionFlags);

CallGraphSection = Ctx->getELFSection(".callgraph", ELF::SHT_PROGBITS, 0);

StackSizesSection = Ctx->getELFSection(".stack_sizes", ELF::SHT_PROGBITS, 0);

PseudoProbeSection = Ctx->getELFSection(".pseudo_probe", DebugSecType, 0);
Expand Down Expand Up @@ -1076,6 +1078,24 @@ MCSection *MCObjectFileInfo::getDwarfComdatSection(const char *Name,
llvm_unreachable("Unknown ObjectFormatType");
}

MCSection *
MCObjectFileInfo::getCallGraphSection(const MCSection &TextSec) const {
if (Ctx->getObjectFileType() != MCContext::IsELF)
return CallGraphSection;

const MCSectionELF &ElfSec = static_cast<const MCSectionELF &>(TextSec);
unsigned Flags = ELF::SHF_LINK_ORDER;
StringRef GroupName;
if (const MCSymbol *Group = ElfSec.getGroup()) {
GroupName = Group->getName();
Flags |= ELF::SHF_GROUP;
}

return Ctx->getELFSection(".callgraph", ELF::SHT_PROGBITS, Flags, 0,
GroupName, true, ElfSec.getUniqueID(),
cast<MCSymbolELF>(TextSec.getBeginSymbol()));
}

MCSection *
MCObjectFileInfo::getStackSizesSection(const MCSection &TextSec) const {
if ((Ctx->getObjectFileType() != MCContext::IsELF) ||
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8739,6 +8739,7 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI,
bool &IsTailCall = CLI.IsTailCall;
CallingConv::ID &CallConv = CLI.CallConv;
bool IsVarArg = CLI.IsVarArg;
const auto *CB = CLI.CB;

MachineFunction &MF = DAG.getMachineFunction();
MachineFunction::CallSiteInfo CSInfo;
Expand Down Expand Up @@ -8778,6 +8779,10 @@ AArch64TargetLowering::LowerCall(CallLoweringInfo &CLI,
*DAG.getContext());
RetCCInfo.AnalyzeCallResult(Ins, RetCC);

// Set type id for call site info.
if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall())
CSInfo = MachineFunction::CallSiteInfo(*CB);

// Check callee args/returns for SVE registers and set calling convention
// accordingly.
if (CallConv == CallingConv::C || CallConv == CallingConv::Fast) {
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/ARM/ARMISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2431,6 +2431,7 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
CallingConv::ID CallConv = CLI.CallConv;
bool doesNotRet = CLI.DoesNotReturn;
bool isVarArg = CLI.IsVarArg;
const auto *CB = CLI.CB;

MachineFunction &MF = DAG.getMachineFunction();
ARMFunctionInfo *AFI = MF.getInfo<ARMFunctionInfo>();
Expand All @@ -2454,6 +2455,10 @@ ARMTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
!Subtarget->noBTIAtReturnTwice())
GuardWithBTI = AFI->branchTargetEnforcement();

// Set type id for call site info.
if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall())
CSInfo = MachineFunction::CallSiteInfo(*CB);

// Determine whether this is a non-secure function call.
if (CLI.CB && CLI.CB->getAttributes().hasFnAttr("cmse_nonsecure_call"))
isCmseNSCall = true;
Expand Down
6 changes: 5 additions & 1 deletion llvm/lib/Target/Mips/MipsISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3250,6 +3250,7 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
bool &IsTailCall = CLI.IsTailCall;
CallingConv::ID CallConv = CLI.CallConv;
bool IsVarArg = CLI.IsVarArg;
const auto *CB = CLI.CB;

MachineFunction &MF = DAG.getMachineFunction();
MachineFrameInfo &MFI = MF.getFrameInfo();
Expand Down Expand Up @@ -3306,8 +3307,11 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
// Get a count of how many bytes are to be pushed on the stack.
unsigned StackSize = CCInfo.getStackSize();

// Call site info for function parameters tracking.
// Call site info for function parameters tracking and call base type info.
MachineFunction::CallSiteInfo CSInfo;
// Set type id for call site info.
if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall())
CSInfo = MachineFunction::CallSiteInfo(*CB);

// Check if it's really possible to do a tail call. Restrict it to functions
// that are part of this compilation unit.
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/Target/X86/X86FastISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3631,6 +3631,12 @@ bool X86FastISel::fastLowerCall(CallLoweringInfo &CLI) {
CLI.NumResultRegs = RVLocs.size();
CLI.Call = MIB;

// Add call site info for call graph section.
if (TM.Options.EmitCallGraphSection && CB && CB->isIndirectCall()) {
MachineFunction::CallSiteInfo CSInfo(*CB);
MF->addCallSiteInfo(CLI.Call, std::move(CSInfo));
}

return true;
}

Expand Down Expand Up @@ -4026,6 +4032,8 @@ bool X86FastISel::tryToFoldLoadIntoMI(MachineInstr *MI, unsigned OpNo,
MO.setReg(IndexReg);
}

if (MI->isCall())
FuncInfo.MF->moveCallSiteInfo(MI, Result);
Result->addMemOperand(*FuncInfo.MF, createMachineMemOperandFor(LI));
Result->cloneInstrSymbols(*FuncInfo.MF, *MI);
MachineBasicBlock::iterator I(MI);
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/X86/X86ISelLoweringCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2027,6 +2027,10 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
if (CallConv == CallingConv::X86_INTR)
report_fatal_error("X86 interrupts may not be called directly");

// Set type id for call site info.
if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall())
CSInfo = MachineFunction::CallSiteInfo(*CB);

// Analyze operands of the call, assigning locations to each operand.
SmallVector<CCValAssign, 16> ArgLocs;
CCState CCInfo(CallConv, isVarArg, MF, ArgLocs, *DAG.getContext());
Expand Down
5 changes: 3 additions & 2 deletions llvm/test/Bitcode/operand-bundles-bc-analyzer.ll
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
; CHECK-NEXT: <OPERAND_BUNDLE_TAG
; CHECK-NEXT: <OPERAND_BUNDLE_TAG
; CHECK-NEXT: <OPERAND_BUNDLE_TAG
; CHECK-NEXT: <OPERAND_BUNDLE_TAG
; CHECK-NEXT: </OPERAND_BUNDLE_TAGS_BLOCK

; CHECK: <FUNCTION_BLOCK
Expand All @@ -25,9 +26,9 @@

declare void @callee0()

define void @f0(i32* %ptr) {
define void @f0(ptr %ptr) {
entry:
%l = load i32, i32* %ptr
%l = load i32, ptr %ptr
%x = add i32 42, 1
call void @callee0() [ "foo"(i32 42, i64 100, i32 %x), "bar"(float 0.000000e+00, i64 100, i32 %l) ]
ret void
Expand Down
29 changes: 29 additions & 0 deletions llvm/test/CodeGen/AArch64/call-site-info-typeid.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
;; Tests that call site type ids can be extracted and set from type operand
;; bundles.

;; Verify the exact typeId value to ensure it is not garbage but the value
;; computed as the type id from the type operand bundle.
; RUN: llc --call-graph-section -mtriple aarch64-linux-gnu < %s -stop-before=finalize-isel -o - | FileCheck %s

define dso_local void @foo(i8 signext %a) !type !3 {
entry:
ret void
}

; CHECK: name: main
define dso_local i32 @main() !type !4 {
entry:
%retval = alloca i32, align 4
%fp = alloca ptr, align 8
store i32 0, ptr %retval, align 4
store ptr @foo, ptr %fp, align 8
%0 = load ptr, ptr %fp, align 8
; CHECK: callSites:
; CHECK-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId:
; CHECK-NEXT: 7854600665770582568 }
call void %0(i8 signext 97) [ "type"(metadata !"_ZTSFvcE.generalized") ]
ret i32 0
}

!3 = !{i64 0, !"_ZTSFvcE.generalized"}
!4 = !{i64 0, !"_ZTSFiE.generalized"}
29 changes: 29 additions & 0 deletions llvm/test/CodeGen/ARM/call-site-info-typeid.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
;; Tests that call site type ids can be extracted and set from type operand
;; bundles.

;; Verify the exact typeId value to ensure it is not garbage but the value
;; computed as the type id from the type operand bundle.
; RUN: llc --call-graph-section -mtriple arm-linux-gnu < %s -stop-before=finalize-isel -o - | FileCheck %s

define dso_local void @foo(i8 signext %a) !type !3 {
entry:
ret void
}

; CHECK: name: main
define dso_local i32 @main() !type !4 {
entry:
%retval = alloca i32, align 4
%fp = alloca ptr, align 8
store i32 0, ptr %retval, align 4
store ptr @foo, ptr %fp, align 8
%0 = load ptr, ptr %fp, align 8
; CHECK: callSites:
; CHECK-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId:
; CHECK-NEXT: 7854600665770582568 }
call void %0(i8 signext 97) [ "type"(metadata !"_ZTSFvcE.generalized") ]
ret i32 0
}

!3 = !{i64 0, !"_ZTSFvcE.generalized"}
!4 = !{i64 0, !"_ZTSFiE.generalized"}
102 changes: 102 additions & 0 deletions llvm/test/CodeGen/MIR/X86/call-site-info-typeid.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
;; Test MIR printer and parser for type id field in call site info. Test that
;; it works well with/without --emit-call-site-info.

;; Multiplex --call-graph-section and -emit-call-site-info as both utilize
;; CallSiteInfo and callSites.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test printer and parser with --call-graph-section only.

;; Test printer.
;; Verify that fwdArgRegs is not set, typeId is set.
;; Verify the exact typeId value to ensure it is not garbage but the value
;; computed as the type id from the type operand bundle.
; RUN: llc --call-graph-section %s -stop-before=finalize-isel -o %t1.mir
; RUN: cat %t1.mir | FileCheck %s --check-prefix=PRINTER_CGS
; PRINTER_CGS: name: main
; PRINTER_CGS: callSites:
; PRINTER_CGS-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId:
; PRINTER_CGS-NEXT: 7854600665770582568 }


;; Test parser.
;; Verify that we get the same result.
; RUN: llc --call-graph-section %t1.mir -run-pass=finalize-isel -o - \
; RUN: | FileCheck %s --check-prefix=PARSER_CGS
; PARSER_CGS: name: main
; PARSER_CGS: callSites:
; PARSER_CGS-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId:
; PARSER_CGS-NEXT: 7854600665770582568 }

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test printer and parser with -emit-call-site-info only.

;; Test printer.
;; Verify that fwdArgRegs is set, typeId is not set.
; RUN: llc -emit-call-site-info %s -stop-before=finalize-isel -o %t2.mir
; RUN: cat %t2.mir | FileCheck %s --check-prefix=PRINTER_CSI
; PRINTER_CSI: name: main
; PRINTER_CSI: callSites:
; PRINTER_CSI-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs:
; PRINTER_CSI-NEXT: { arg: 0, reg: '$edi' }
; PRINTER_CSI-NOT: typeId:


;; Test parser.
;; Verify that we get the same result.
; RUN: llc -emit-call-site-info %t2.mir -run-pass=finalize-isel -o - \
; RUN: | FileCheck %s --check-prefix=PARSER_CSI
; PARSER_CSI: name: main
; PARSER_CSI: callSites:
; PARSER_CSI-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs:
; PARSER_CSI-NEXT: { arg: 0, reg: '$edi' }
; PARSER_CSI-NOT: typeId:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test printer and parser with both -emit-call-site-info and --call-graph-section.

;; Test printer.
;; Verify both fwdArgRegs and typeId are set.
;; Verify the exact typeId value to ensure it is not garbage but the value
;; computed as the type id from the type operand bundle.
; RUN: llc --call-graph-section -emit-call-site-info %s -stop-before=finalize-isel -o %t2.mir
; RUN: cat %t2.mir | FileCheck %s --check-prefix=PRINTER_CGS_CSI
; PRINTER_CGS_CSI: name: main
; PRINTER_CGS_CSI: callSites:
; PRINTER_CGS_CSI-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs:
; PRINTER_CGS_CSI-NEXT: { arg: 0, reg: '$edi' }, typeId:
; PRINTER_CGS_CSI-NEXT: 7854600665770582568 }


;; Test parser.
;; Verify that we get the same result.
; RUN: llc --call-graph-section -emit-call-site-info %t2.mir -run-pass=finalize-isel -o - \
; RUN: | FileCheck %s --check-prefix=PARSER_CGS_CSI
; PARSER_CGS_CSI: name: main
; PARSER_CGS_CSI: callSites:
; PARSER_CGS_CSI-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs:
; PARSER_CGS_CSI-NEXT: { arg: 0, reg: '$edi' }, typeId:
; PARSER_CGS_CSI-NEXT: 7854600665770582568 }

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Function Attrs: noinline nounwind optnone uwtable
define dso_local void @foo(i8 signext %a) !type !3 {
entry:
ret void
}

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() !type !4 {
entry:
%retval = alloca i32, align 4
%fp = alloca ptr, align 8
store i32 0, ptr %retval, align 4
store ptr @foo, ptr %fp, align 8
%0 = load ptr, ptr %fp, align 8
call void %0(i8 signext 97) [ "type"(metadata !"_ZTSFvcE.generalized") ]
ret i32 0
}

!3 = !{i64 0, !"_ZTSFvcE.generalized"}
!4 = !{i64 0, !"_ZTSFiE.generalized"}
63 changes: 63 additions & 0 deletions llvm/test/CodeGen/MIR/X86/call-site-info-typeid.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Test MIR printer and parser for type id field in callSites. It is used
# for propogating call site type identifiers to emit in the call graph section.

# RUN: llc --call-graph-section %s -run-pass=none -o - | FileCheck %s
# CHECK: name: main
# CHECK: callSites:
# CHECK-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId:
# CHECK-NEXT: 123456789 }

--- |
define dso_local void @foo(i8 signext %a) {
entry:
ret void
}

define dso_local i32 @main() {
entry:
%retval = alloca i32, align 4
%fp = alloca ptr, align 8
store i32 0, ptr %retval, align 4
store ptr @foo, ptr %fp, align 8
%0 = load ptr, ptr %fp, align 8
call void %0(i8 signext 97)
ret i32 0
}

...
---
name: foo
tracksRegLiveness: true
body: |
bb.0.entry:
RET 0

...
---
name: main
tracksRegLiveness: true
stack:
- { id: 0, name: retval, type: default, offset: 0, size: 4, alignment: 4,
stack-id: default, callee-saved-register: '', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 1, name: fp, type: default, offset: 0, size: 8, alignment: 8,
stack-id: default, callee-saved-register: '', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
callSites:
- { bb: 0, offset: 6, fwdArgRegs: [], typeId:
123456789 }
body: |
bb.0.entry:
MOV32mi %stack.0.retval, 1, $noreg, 0, $noreg, 0 :: (store (s32) into %ir.retval)
MOV64mi32 %stack.1.fp, 1, $noreg, 0, $noreg, @foo :: (store (s64) into %ir.fp)
%0:gr64 = MOV32ri64 @foo
ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
%1:gr32 = MOV32ri 97
$edi = COPY %1
CALL64r killed %0, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit-def $rsp, implicit-def $ssp
ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp
%2:gr32 = MOV32r0 implicit-def dead $eflags
$eax = COPY %2
RET 0, $eax

...
29 changes: 29 additions & 0 deletions llvm/test/CodeGen/Mips/call-site-info-typeid.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
;; Tests that call site type ids can be extracted and set from type operand
;; bundles.

;; Verify the exact typeId value to ensure it is not garbage but the value
;; computed as the type id from the type operand bundle.
; RUN: llc --call-graph-section -mtriple=mips-linux-gnu < %s -stop-before=finalize-isel -o - | FileCheck %s

define dso_local void @foo(i8 signext %a) !type !3 {
entry:
ret void
}

; CHECK: name: main
define dso_local i32 @main() !type !4 {
entry:
%retval = alloca i32, align 4
%fp = alloca ptr, align 8
store i32 0, ptr %retval, align 4
store ptr @foo, ptr %fp, align 8
%0 = load ptr, ptr %fp, align 8
; CHECK: callSites:
; CHECK-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId:
; CHECK-NEXT: 7854600665770582568 }
call void %0(i8 signext 97) [ "type"(metadata !"_ZTSFvcE.generalized") ]
ret i32 0
}

!3 = !{i64 0, !"_ZTSFvcE.generalized"}
!4 = !{i64 0, !"_ZTSFiE.generalized"}
29 changes: 29 additions & 0 deletions llvm/test/CodeGen/X86/call-site-info-typeid.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
;; Tests that call site type ids can be extracted and set from type operand
;; bundles.

;; Verify the exact typeId value to ensure it is not garbage but the value
;; computed as the type id from the type operand bundle.
; RUN: llc --call-graph-section -mtriple=x86_64-unknown-linux < %s -stop-before=finalize-isel -o - | FileCheck %s

define dso_local void @foo(i8 signext %a) !type !3 {
entry:
ret void
}

; CHECK: name: main
define dso_local i32 @main() !type !4 {
entry:
%retval = alloca i32, align 4
%fp = alloca ptr, align 8
store i32 0, ptr %retval, align 4
store ptr @foo, ptr %fp, align 8
%0 = load ptr, ptr %fp, align 8
; CHECK: callSites:
; CHECK-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId:
; CHECK-NEXT: 7854600665770582568 }
call void %0(i8 signext 97) [ "type"(metadata !"_ZTSFvcE.generalized") ]
ret i32 0
}

!3 = !{i64 0, !"_ZTSFvcE.generalized"}
!4 = !{i64 0, !"_ZTSFiE.generalized"}
73 changes: 73 additions & 0 deletions llvm/test/CodeGen/call-graph-section.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
; Tests that we store the type identifiers in .callgraph section of the binary.

; RUN: llc --call-graph-section -filetype=obj -o - < %s | \
; RUN: llvm-readelf -x .callgraph - | FileCheck %s

target triple = "x86_64-unknown-linux-gnu"

define dso_local void @foo() #0 !type !4 {
entry:
ret void
}

define dso_local i32 @bar(i8 signext %a) #0 !type !5 {
entry:
%a.addr = alloca i8, align 1
store i8 %a, i8* %a.addr, align 1
ret i32 0
}

define dso_local i32* @baz(i8* %a) #0 !type !6 {
entry:
%a.addr = alloca i8*, align 8
store i8* %a, i8** %a.addr, align 8
ret i32* null
}

define dso_local i32 @main() #0 !type !7 {
entry:
%retval = alloca i32, align 4
%fp_foo = alloca void (...)*, align 8
%a = alloca i8, align 1
%fp_bar = alloca i32 (i8)*, align 8
%fp_baz = alloca i32* (i8*)*, align 8
store i32 0, i32* %retval, align 4
store void (...)* bitcast (void ()* @foo to void (...)*), void (...)** %fp_foo, align 8
%0 = load void (...)*, void (...)** %fp_foo, align 8
call void (...) %0() [ "type"(metadata !"_ZTSFvE.generalized") ]
store i32 (i8)* @bar, i32 (i8)** %fp_bar, align 8
%1 = load i32 (i8)*, i32 (i8)** %fp_bar, align 8
%2 = load i8, i8* %a, align 1
%call = call i32 %1(i8 signext %2) [ "type"(metadata !"_ZTSFicE.generalized") ]
store i32* (i8*)* @baz, i32* (i8*)** %fp_baz, align 8
%3 = load i32* (i8*)*, i32* (i8*)** %fp_baz, align 8
%call1 = call i32* %3(i8* %a) [ "type"(metadata !"_ZTSFPvS_E.generalized") ]
call void @foo() [ "type"(metadata !"_ZTSFvE.generalized") ]
%4 = load i8, i8* %a, align 1
%call2 = call i32 @bar(i8 signext %4) [ "type"(metadata !"_ZTSFicE.generalized") ]
%call3 = call i32* @baz(i8* %a) [ "type"(metadata !"_ZTSFPvS_E.generalized") ]
ret i32 0
}

attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}

; Check that the numeric type id (md5 hash) for the below type ids are emitted
; to the callgraph section.

; CHECK: Hex dump of section '.callgraph':

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"uwtable", i32 1}
!2 = !{i32 7, !"frame-pointer", i32 2}
!3 = !{!"clang version 13.0.0 (git@github.com:llvm/llvm-project.git 6d35f403b91c2f2c604e23763f699d580370ca96)"}
; CHECK-DAG: 2444f731 f5eecb3e
!4 = !{i64 0, !"_ZTSFvE.generalized"}
; CHECK-DAG: 5486bc59 814b8e30
!5 = !{i64 0, !"_ZTSFicE.generalized"}
; CHECK-DAG: 7ade6814 f897fd77
!6 = !{i64 0, !"_ZTSFPvS_E.generalized"}
; CHECK-DAG: caaf769a 600968fa
!7 = !{i64 0, !"_ZTSFiE.generalized"}
13 changes: 13 additions & 0 deletions llvm/test/Verifier/operand-bundles.ll
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,17 @@ declare ptr @objc_retainAutoreleasedReturnValue(ptr)
declare ptr @objc_unsafeClaimAutoreleasedReturnValue(ptr)
declare void @llvm.assume(i1)

define void @f_type(i32* %ptr) {
; CHECK: Multiple "type" operand bundles
; CHECK-NEXT: call void @g() [ "type"(metadata !"_ZTSFvE.generalized"), "type"(metadata !"_ZTSFvE.generalized") ]
; CHECK-NOT: call void @g() [ "type"(metadata !"_ZTSFvE.generalized") ]

entry:
%l = load i32, i32* %ptr
call void @g() [ "type"(metadata !"_ZTSFvE.generalized"), "type"(metadata !"_ZTSFvE.generalized") ]
call void @g() [ "type"(metadata !"_ZTSFvE.generalized") ]
%x = add i32 42, 1
ret void
}

attributes #0 = { noreturn }