Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 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: 18 additions & 0 deletions llvm/docs/CallGraphSection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# .callgraph Section Layout

The `.callgraph` section is used to store call graph information for each function. The section contains a series of records, with each record corresponding to a single function.

## Per Function Record Layout

Each record in the `.callgraph` section has the following binary layout:

| Field | Type | Size (bits) | Description |
| -------------------------------------- | ------------- | ----------- | ------------------------------------------------------------------------------------------------------- |
| Format Version | `uint8_t` | 8 | The version of the record format. The current version is 0. |
| Flags | `uint8_t` | 8 | A bitfield where: Bit 0 is set if the function is a potential indirect call target; Bit 1 is set if there are direct callees; Bit 2 is set if there are indirect callees. The remaining 5 bits are reserved. |
| Function Entry PC | `uintptr_t` | 32/64 | The address of the function's entry point. |
| Function Type ID | `uint64_t` | 64 | The type ID of the function. This field is non-zero if the function is a potential indirect call target and its type is known. |
| Number of Unique Direct Callees | `ULEB128` | Variable | The number of unique direct call destinations from this function. This field is only present if there is at least one direct callee. |
| Direct Callees Array | `uintptr_t[]` | Variable | An array of unique direct callee entry point addresses. |
| Number of Unique Indirect Target Type IDs| `ULEB128` | Variable | The number of unique indirect call target type IDs. This field is only present if there is at least one indirect target type ID. |
| Indirect Target Type IDs Array | `uint64_t[]` | Variable | An array of unique indirect call target type IDs. |
7 changes: 7 additions & 0 deletions llvm/docs/CodeGenerator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1662,6 +1662,13 @@ and stack sizes (unsigned LEB128). The stack size values only include the space
allocated in the function prologue. Functions with dynamic stack allocations are
not included.

Emitting function call graph information
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A section containing metadata on function call graph will be emitted when
``TargetOptions::EmitCallGraphSection`` is set (--call-graph-section). Layout of
this section is documented in detail at :doc:`CallGraphSection`.

VLIW Packetizer
---------------

Expand Down
1 change: 1 addition & 0 deletions llvm/docs/Reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ LLVM and API reference documentation.
BranchWeightMetadata
Bugpoint
CalleeTypeMetadata
CallGraphSection
CIBestPractices
CommandGuide/index
ContentAddressableStorage
Expand Down
27 changes: 7 additions & 20 deletions llvm/include/llvm/CodeGen/AsmPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,26 +198,13 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
/// and targets.
using CGTypeId = uint64_t;

/// Map type identifiers to callsite labels. Labels are generated for each
/// indirect callsite in the function.
SmallVector<std::pair<CGTypeId, MCSymbol *>> CallSiteLabels;
/// Unique target type IDs.
SmallSet<CGTypeId, 4> IndirectCalleeTypeIDs;
/// Unique direct callees.
SmallSet<MCSymbol *, 4> DirectCallees;
};

/// Enumeration of function kinds, and their mapping to function kind values
/// stored in callgraph section entries.
enum class FunctionKind : uint64_t {
/// 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,
};

enum CallGraphSectionFormatVersion : uint64_t {
enum CallGraphSectionFormatVersion : uint8_t {
V_0 = 0,
};

Expand Down Expand Up @@ -386,9 +373,9 @@ class LLVM_ABI AsmPrinter : public MachineFunctionPass {
/// are available. Returns empty string otherwise.
StringRef getConstantSectionSuffix(const Constant *C) const;

/// Iff MI is an indirect call, generate and emit a label after the callsites
/// which will be used to populate the .callgraph section. For direct
/// callsites add the callee symbol to direct callsites list of FuncCGInfo.
/// If MI is an indirect call, add expected type IDs to indirect type ids
/// list. If MI is a direct call add the callee symbol to direct callsites
/// list of FuncCGInfo.
void handleCallsiteForCallgraph(
FunctionCallGraphInfo &FuncCGInfo,
const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
Expand Down
98 changes: 48 additions & 50 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1683,63 +1683,64 @@ void AsmPrinter::emitCallGraphSection(const MachineFunction &MF,
OutStreamer->pushSection();
OutStreamer->switchSection(FuncCGSection);

// Emit format version number.
OutStreamer->emitInt64(CallGraphSectionFormatVersion::V_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());

const Function &F = MF.getFunction();
// 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(
static_cast<uint64_t>(FunctionKind::NOT_INDIRECT_TARGET));
} else {
if (const auto *TypeId = extractNumericCGTypeId(F)) {
OutStreamer->emitInt64(
static_cast<uint64_t>(FunctionKind::INDIRECT_TARGET_KNOWN_TID));
OutStreamer->emitInt64(TypeId->getZExtValue());
} else {
OutStreamer->emitInt64(
static_cast<uint64_t>(FunctionKind::INDIRECT_TARGET_UNKNOWN_TID));
}
}
const auto &DirectCallees = FuncCGInfo.DirectCallees;
const auto &IndirectCalleeTypeIDs = FuncCGInfo.IndirectCalleeTypeIDs;

uint8_t Flags = 0;
if (IsIndirectTarget)
Flags |= 1u << 0; // Set the first LSB bit to 1.
if (DirectCallees.size() > 0)
Flags |= 1u << 1; // Set the second LSB bit to 1.
if (IndirectCalleeTypeIDs.size() > 0)
Flags |= 1u << 2; // Set the third LSB bit to 1.

// Emit function's call graph information.
// 1) CallGraphSectionFormatVersion
// 2) Flags
// a. LSB bit 0 is set to 1 if the function is a potential indirect
// target.
// b. LSB bit 1 is set to 1 if there are direct callees.
// c. LSB bit 2 is set to 1 if there are indirect callees.
// d. Rest of the 5 bits in Flags are reserved for any future use.
// 3) Function entry PC.
// 4) FunctionTypeID if the function is indirect target and its type id
// is known, otherwise it is set to 0.
// 5) Number of unique direct callees, if at least one exists.
// 6) For each unique direct callee, the callee's PC.
// 7) Number of unique indirect target type IDs, if at least one exists.
// 8) Each unique indirect target type id.
OutStreamer->emitInt8(CallGraphSectionFormatVersion::V_0);
OutStreamer->emitInt8(Flags);
OutStreamer->emitSymbolValue(FunctionSymbol, TM.getProgramPointerSize());
const auto *TypeId = extractNumericCGTypeId(F);
if (IsIndirectTarget && TypeId)
OutStreamer->emitInt64(TypeId->getZExtValue());
else
OutStreamer->emitInt64(0);

// Emit callsite labels, where each element is a pair of type id and
// indirect callsite pc.
const auto &CallSiteLabels = FuncCGInfo.CallSiteLabels;
OutStreamer->emitInt64(CallSiteLabels.size());
for (const auto &[TypeId, Label] : CallSiteLabels) {
OutStreamer->emitInt64(TypeId);
OutStreamer->emitSymbolValue(Label, TM.getProgramPointerSize());
if (DirectCallees.size() > 0) {
OutStreamer->emitULEB128IntValue(DirectCallees.size());
for (const auto &CalleeSymbol : DirectCallees)
OutStreamer->emitSymbolValue(CalleeSymbol, TM.getProgramPointerSize());
FuncCGInfo.DirectCallees.clear();
}
FuncCGInfo.CallSiteLabels.clear();

const auto &DirectCallees = FuncCGInfo.DirectCallees;
OutStreamer->emitInt64(DirectCallees.size());
for (const auto &CalleeSymbol : DirectCallees) {
OutStreamer->emitSymbolValue(CalleeSymbol, TM.getProgramPointerSize());
if (IndirectCalleeTypeIDs.size() > 0) {
OutStreamer->emitULEB128IntValue(IndirectCalleeTypeIDs.size());
for (const auto &CalleeTypeId : IndirectCalleeTypeIDs)
OutStreamer->emitInt64(CalleeTypeId);
FuncCGInfo.IndirectCalleeTypeIDs.clear();
}
FuncCGInfo.DirectCallees.clear();

// End of emitting call graph section contents.
OutStreamer->popSection();
}

Expand Down Expand Up @@ -1877,8 +1878,7 @@ void AsmPrinter::handleCallsiteForCallgraph(
FunctionCallGraphInfo &FuncCGInfo,
const MachineFunction::CallSiteInfoMap &CallSitesInfoMap,
const MachineInstr &MI) {
assert(MI.isCall() &&
"Callsite labels are meant for call instructions only.");
assert(MI.isCall() && "This method is meant for call instructions only.");
const MachineOperand &CalleeOperand = MI.getOperand(0);
if (CalleeOperand.isGlobal() || CalleeOperand.isSymbol()) {
// Handle direct calls.
Expand All @@ -1903,10 +1903,8 @@ void AsmPrinter::handleCallsiteForCallgraph(
// Handle indirect callsite info.
// Only indirect calls have type identifiers set.
for (ConstantInt *CalleeTypeId : CallSiteInfo->second.CalleeTypeIds) {
MCSymbol *S = MF->getContext().createTempSymbol();
OutStreamer->emitLabel(S);
uint64_t CalleeTypeIdVal = CalleeTypeId->getZExtValue();
FuncCGInfo.CallSiteLabels.emplace_back(CalleeTypeIdVal, S);
FuncCGInfo.IndirectCalleeTypeIDs.insert(CalleeTypeIdVal);
}
}

Expand Down
87 changes: 59 additions & 28 deletions llvm/test/CodeGen/X86/call-graph-section-assembly.ll
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,88 @@
;; call sites annotated with !callee_type metadata.
;; Test if the .callgraph section contains unique direct callees.

; RUN: llc -mtriple=x86_64-unknown-linux --call-graph-section -o - < %s | FileCheck %s
; REQUIRES: x86-registered-target
; REQUIRES: arm-registered-target

; RUN: llc -mtriple=x86_64-unknown-linux --call-graph-section -o - < %s | FileCheck --check-prefix=X64 %s
; RUN: llc -mtriple=arm-unknown-linux --call-graph-section -o - < %s | FileCheck --check-prefix=ARM32 %s

declare !type !0 void @direct_foo()
declare !type !1 i32 @direct_bar(i8)
declare !type !2 ptr @direct_baz(ptr)

; CHECK: ball:
; CHECK-NEXT: [[LABEL_FUNC:\.Lfunc_begin[0-9]+]]:
; X64: ball:
; X64-NEXT: [[LABEL_FUNC:\.Lfunc_begin[0-9]+]]:
; ARM32: ball:
; ARM32-NEXT: [[LABEL_FUNC:\.Lfunc_begin[0-9]+]]:
define ptr @ball() {
entry:
call void @direct_foo()
%fp_foo_val = load ptr, ptr null, align 8
; CHECK: [[LABEL_TMP0:\.L.*]]:
%fp_foo_val = load ptr, ptr null, align 8
call void (...) %fp_foo_val(), !callee_type !0
call void @direct_foo()
%fp_bar_val = load ptr, ptr null, align 8
; CHECK: [[LABEL_TMP1:\.L.*]]:
%fp_bar_val = load ptr, ptr null, align 8
%call_fp_bar = call i32 %fp_bar_val(i8 0), !callee_type !2
%call_fp_bar_direct = call i32 @direct_bar(i8 1)
%fp_baz_val = load ptr, ptr null, align 8
; CHECK: [[LABEL_TMP2:\.L.*]]:
%call_fp_baz = call ptr %fp_baz_val(ptr null), !callee_type !4
call void @direct_foo()
%call_fp_baz_direct = call ptr @direct_baz(ptr null)
call void @direct_foo()
ret ptr %call_fp_baz
}

; CHECK: .section .callgraph,"o",@progbits,.text

; CHECK-NEXT: .quad 0
; CHECK-NEXT: .quad [[LABEL_FUNC]]
; CHECK-NEXT: .quad 1
; CHECK-NEXT: .quad 3
!0 = !{!1}
!1 = !{i64 0, !"_ZTSFvE.generalized"}
;; Test for MD5 hash of _ZTSFvE.generalized and the generated temporary callsite label.
; CHECK-NEXT: .quad 4524972987496481828
; CHECK-NEXT: .quad [[LABEL_TMP0]]
!2 = !{!3}
!3 = !{i64 0, !"_ZTSFicE.generalized"}
;; Test for MD5 hash of _ZTSFicE.generalized and the generated temporary callsite label.
; CHECK-NEXT: .quad 3498816979441845844
; CHECK-NEXT: .quad [[LABEL_TMP1]]
!4 = !{!5}
!5 = !{i64 0, !"_ZTSFPvS_E.generalized"}
;; Test for MD5 hash of _ZTSFPvS_E.generalized and the generated temporary callsite label.
; CHECK-NEXT: .quad 8646233951371320954
; CHECK-NEXT: .quad [[LABEL_TMP2]]
;; Test for number of direct calls and {callsite_label, callee} pairs.
; CHECK-NEXT: .quad 3
; CHECK-NEXT: .quad direct_foo
; CHECK-NEXT: .quad direct_bar
; CHECK-NEXT: .quad direct_baz

; X64: .section .callgraph,"o",@progbits,.text
;; Version
; X64-NEXT: .byte 0
;; Flags
; X64-NEXT: .byte 7
;; Function Entry PC
; X64-NEXT: .quad [[LABEL_FUNC]]
;; Function type ID -- set to 0 as no type metadata attached to function.
; X64-NEXT: .quad 0
;; Number of unique direct callees.
; X64-NEXT: .byte 3
;; Direct callees.
; X64-NEXT: .quad direct_foo
; X64-NEXT: .quad direct_bar
; X64-NEXT: .quad direct_baz
;; Number of unique indirect target type IDs.
; X64-NEXT: .byte 3
;; Indirect type IDs.
; X64-NEXT: .quad 4524972987496481828
; X64-NEXT: .quad 3498816979441845844
; X64-NEXT: .quad 8646233951371320954

; ARM32: .section .callgraph,"o",%progbits,.text
;; Version
; ARM32-NEXT: .byte 0
;; Flags
; ARM32-NEXT: .byte 7
;; Function Entry PC
; ARM32-NEXT: .long [[LABEL_FUNC]]
;; Function type ID -- set to 0 as no type metadata attached to function.
; ARM32-NEXT: .long 0
; ARM32-NEXT: .long 0
;; Number of unique direct callees.
; ARM32-NEXT: .byte 3
;; Direct callees.
; ARM32-NEXT: .long direct_foo
; ARM32-NEXT: .long direct_bar
; ARM32-NEXT: .long direct_baz
;; Number of unique indirect target type IDs.
; ARM32-NEXT: .byte 3
;; Indirect type IDs.
; ARM32-NEXT: .long 838288420
; ARM32-NEXT: .long 1053552373
; ARM32-NEXT: .long 1505527380
; ARM32-NEXT: .long 814631809
; ARM32-NEXT: .long 342417018
; ARM32-NEXT: .long 2013108216
26 changes: 19 additions & 7 deletions llvm/test/CodeGen/X86/call-graph-section-tailcall.ll
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
;; Tests that we store the type identifiers in .callgraph section of the object file for tailcalls.

; REQUIRES: x86-registered-target
; REQUIRES: arm-registered-target

; RUN: llc -mtriple=x86_64-unknown-linux --call-graph-section -filetype=obj -o - < %s | \
; RUN: llvm-readelf -x .callgraph - | FileCheck %s
; RUN: llvm-readelf -x .callgraph - | FileCheck --check-prefix=X64 %s
; RUN: llc -mtriple=arm-unknown-linux --call-graph-section -filetype=obj -o - < %s | \
; RUN: llvm-readelf -x .callgraph - | FileCheck --check-prefix=ARM32 %s

define i32 @check_tailcall(ptr %func, i8 %x) !type !0 {
entry:
Expand All @@ -22,13 +27,20 @@ declare !type !2 i32 @foo(i8 signext)

declare !type !2 i32 @bar(i8 signext)

;; 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 = !{i64 0, !"_ZTSFiPvcE.generalized"}
!1 = !{!2}
; CHECK-DAG: 5486bc59 814b8e30
!2 = !{i64 0, !"_ZTSFicE.generalized"}
!3 = !{i64 0, !"_ZTSFiiE.generalized"}

; X64: Hex dump of section '.callgraph':
; X64-NEXT: 0x00000000 00050000 00000000 00008e19 0b7f3326
; X64-NEXT: 0x00000010 e3000154 86bc5981 4b8e3000 05000000
;; Verify that the type id 0x308e4b8159bc8654 is in section.
; X64-NEXT: 0x00000020 00000000 00a150b8 3e0cfe3c b2015486
; X64-NEXT: 0x00000030 bc59814b 8e30

; ARM32: Hex dump of section '.callgraph':
; ARM32-NEXT: 0x00000000 00050000 00008e19 0b7f3326 e3000154
; ARM32-NEXT: 0x00000010 86bc5981 4b8e3000 05100000 00a150b8
;; Verify that the type id 0x308e4b8159bc8654 is in section.
; ARM32-NEXT: 0x00000020 3e0cfe3c b2015486 bc59814b 8e30
Loading