Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6322,6 +6322,8 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
DI->EmitFuncDeclForCallSite(
CI, DI->getFunctionType(CalleeDecl, ResTy, Args), CalleeGlobalDecl);
}
// Generate call site target information.
DI->addCallTargetIfVirtual(CalleeDecl, CI);
}

return Ret;
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4978,6 +4978,23 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc,
Fn->setSubprogram(SP);
}

void CGDebugInfo::addCallTargetIfVirtual(const FunctionDecl *FD,
llvm::CallBase *CI) {
if (!shouldGenerateVirtualCallSite())
return;

if (!FD)
return;

assert(CI && "Invalid Call Instruction.");
if (!CI->isIndirectCall())
return;

// Always get the method declaration.
if (llvm::DISubprogram *MD = getFunctionDeclaration(FD))
CI->setMetadata(llvm::LLVMContext::MD_call_target, MD);
}

void CGDebugInfo::EmitFuncDeclForCallSite(llvm::CallBase *CallOrInvoke,
QualType CalleeType,
GlobalDecl CalleeGlobalDecl) {
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,9 @@ class CGDebugInfo {
/// that it is supported and enabled.
llvm::DINode::DIFlags getCallSiteRelatedAttrs() const;

/// Add call target information.
void addCallTargetIfVirtual(const FunctionDecl *FD, llvm::CallBase *CI);

private:
/// Amend \p I's DebugLoc with \p Group (its source atom group) and \p
/// Rank (lower nonzero rank is higher precedence). Does nothing if \p I
Expand Down Expand Up @@ -906,6 +909,12 @@ class CGDebugInfo {
/// If one exists, returns the linkage name of the specified \
/// (non-null) \c Method. Returns empty string otherwise.
llvm::StringRef GetMethodLinkageName(const CXXMethodDecl *Method) const;

/// Returns true if we should generate call target information.
bool shouldGenerateVirtualCallSite() const {
// Check general conditions for call site generation.
return (getCallSiteRelatedAttrs() != llvm::DINode::FlagZero);
}
};

/// A scoped helper to set the current debug location to the specified
Expand Down
48 changes: 48 additions & 0 deletions clang/test/DebugInfo/CXX/callsite-base.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// RUN: %clang_cc1 -triple=x86_64-linux -disable-llvm-passes -emit-llvm \
// RUN: -debug-info-kind=standalone -dwarf-version=5 -O1 %s \
// RUN: -o - | FileCheck %s -check-prefix CHECK-BASE

// Simple class with only virtual methods: inlined and not-inlined
//
// The following three scenarios are considered:
// - out-of-line defined virtual member function (f1)
// - declared-but-not-defined virtual member function (f2)
// - inline defined virtual member function (f3)
//
// 1) We check for a generated 'call_target' for: 'f1', 'f2' and 'f3'.
// 2) Check that the 'CBase' type is defined.

struct CBase {
virtual void f1();
virtual void f2();
virtual void f3() {}
};
void CBase::f1() {}

void bar(CBase *Base) {
Base->f1();
Base->f2();
Base->f3();

// Because this will instantiate the ctor, the CBase type should be defined.
CBase B;
B.f1();
}

// CHECK-BASE: %struct.CBase = type { ptr }

// CHECK-BASE: define {{.*}} @_Z3barP5CBase{{.*}} {
// CHECK-BASE: alloca %struct.CBase
// CHECK-BASE: call void %1{{.*}} !dbg {{![0-9]+}}, !call_target [[BASE_F1_DCL:![0-9]+]]
// CHECK-BASE: call void %3{{.*}} !dbg {{![0-9]+}}, !call_target [[BASE_F2_DCL:![0-9]+]]
// CHECK-BASE: call void %5{{.*}} !dbg {{![0-9]+}}, !call_target [[BASE_F3_DCL:![0-9]+]]
// CHECK-BASE: call void @_ZN5CBaseC1Ev{{.*}} !dbg {{![0-9]+}}
// CHECK-BASE: call void @_ZN5CBase2f1Ev{{.*}} !dbg {{![0-9]+}}
// CHECK-BASE: }

// CHECK-BASE: [[BASE_F1_DCL]] = {{.*}}!DISubprogram(name: "f1", linkageName: "_ZN5CBase2f1Ev", {{.*}}containingType
// CHECK-BASE: [[BASE_F2_DCL]] = {{.*}}!DISubprogram(name: "f2", linkageName: "_ZN5CBase2f2Ev", {{.*}}containingType
// CHECK-BASE: [[BASE_F3_DCL]] = {{.*}}!DISubprogram(name: "f3", linkageName: "_ZN5CBase2f3Ev", {{.*}}containingType

// CHECK-BASE: [[BASE_F1_DEF:![0-9]+]] = {{.*}}!DISubprogram(name: "f1", linkageName: "_ZN5CBase2f1Ev", {{.*}}DISPFlagDefinition
// CHECK-BASE: [[BASE_F3_DEF:![0-9]+]] = {{.*}}!DISubprogram(name: "f3", linkageName: "_ZN5CBase2f3Ev", {{.*}}DISPFlagDefinition
58 changes: 58 additions & 0 deletions clang/test/DebugInfo/CXX/callsite-derived.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// RUN: %clang_cc1 -triple=x86_64-linux -disable-llvm-passes -emit-llvm \
// RUN: -debug-info-kind=constructor -dwarf-version=5 -O1 %s \
// RUN: -o - | FileCheck %s -check-prefix CHECK-DERIVED

// Simple base and derived class with virtual and static methods:
// We check for:
// - a generated 'call_target' for 'f1'.
// - not generated 'call_target' for 'f3'.

struct CBase {
virtual void f1() {}
static void f3();
};

void CBase::f3() {
}

void foo(CBase *Base) {
CBase::f3();
}

struct CDerived : public CBase {
void f1() {}
};
void foo(CDerived *Derived);

int main() {
CDerived D;
foo(&D);

return 0;
}

void foo(CDerived *Derived) {
Derived->f1();
}

// CHECK-DERIVED: define {{.*}} @_Z3fooP5CBase{{.*}} {
// CHECK-DERIVED: call void @_ZN5CBase2f3Ev{{.*}} !dbg {{![0-9]+}}
// CHECK-DERIVED: }

// CHECK-DERIVED: define {{.*}} @main{{.*}} {
// CHECK-DERIVED: call void @_ZN8CDerivedC1Ev{{.*}} !dbg {{![0-9]+}}
// CHECK-DERIVED: call void @_Z3fooP8CDerived{{.*}} !dbg {{![0-9]+}}
// CHECK-DERIVED: }

// CHECK-DERIVED: define {{.*}} @_ZN8CDerivedC1Ev{{.*}} {
// CHECK-DERIVED: call void @_ZN8CDerivedC2Ev{{.*}} !dbg {{![0-9]+}}
// CHECK-DERIVED: }

// CHECK-DERIVED: define {{.*}} @_Z3fooP8CDerived{{.*}} {
// CHECK-DERIVED: call void %1{{.*}} !dbg {{![0-9]+}}, !call_target [[DERIVED_F1_DCL:![0-9]+]]
// CHECK-DERIVED: }

// CHECK-DERIVED: [[BASE_F1_DCL:![0-9]+]] = {{.*}}!DISubprogram(name: "f1", linkageName: "_ZN5CBase2f1Ev", {{.*}}containingType
// CHECK-DERIVED: [[DERIVED_F1_DCL]] = {{.*}}!DISubprogram(name: "f1", linkageName: "_ZN8CDerived2f1Ev", {{.*}}containingType
// CHECK-DERIVED: [[DERIVED_F1_DEF:![0-9]+]] = {{.*}}!DISubprogram(name: "f1", linkageName: "_ZN8CDerived2f1Ev", {{.*}}DISPFlagDefinition
// CHECK-DERIVED: [[BASE_F1_DEF:![0-9]+]] = {{.*}}!DISubprogram(name: "f1", linkageName: "_ZN5CBase2f1Ev", {{.*}}DISPFlagDefinition
93 changes: 93 additions & 0 deletions clang/test/DebugInfo/CXX/callsite-edges.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// RUN: %clang_cc1 -triple=x86_64-linux -disable-llvm-passes -emit-llvm \
// RUN: -debug-info-kind=constructor -dwarf-version=5 -O1 %s \
// RUN: -o - | FileCheck %s -check-prefix CHECK-EDGES

// The following are identified edge cases involving the method being called:
// 1) Method is declared but not defined in current CU.
// 2) Pure virtual method but not defined in current CU.
// 3) Virtual method defined in a deeply nested structure hierarchy.

//---------------------------------------------------------------------
// 1) Method is declared but not defined in current CU - Pass.
// Generate 'call_target' metadata for 'f1' and 'f2'.
//---------------------------------------------------------------------
struct CEmpty {
virtual void f1();
virtual void f2();
};

void CEmpty::f2() {
}

void edge_a(CEmpty *Empty) {
Empty->f1();
Empty->f2();
}

//---------------------------------------------------------------------
// 2) Pure virtual method but not defined in current CU - Pass.
// Generate 'call_target' metadata for 'f1' and 'f2'.
//---------------------------------------------------------------------
struct CBase {
virtual void f1() = 0;
virtual void f2();
};

void CBase::f2() {
}

void edge_b(CBase *Base) {
Base->f1();
Base->f2();
}

//---------------------------------------------------------------------
// 3) Virtual method defined in a deeply nested structure hierarchy - Pass.
// Generate 'call_target' metadata for 'd0', 'd1', 'd2' and 'd3'.
//---------------------------------------------------------------------
struct CD0 {
struct CD1 {
virtual void d1();
};

CD1 D1;
virtual void d0();
};

void CD0::d0() {}
void CD0::CD1::d1() {}

void edge_c(CD0 *D0) {
D0->d0();

CD0::CD1 *D1 = &D0->D1;
D1->d1();
}

// CHECK-EDGES: define {{.*}} @_Z6edge_aP6CEmpty{{.*}} {
// CHECK-EDGES: call void %1{{.*}} !dbg {{![0-9]+}}, !call_target [[CEMPTY_F1_DCL:![0-9]+]]
// CHECK-EDGES: call void %3{{.*}} !dbg {{![0-9]+}}, !call_target [[CEMPTY_F2_DCL:![0-9]+]]
// CHECK-EDGES: }

// CHECK-EDGES: define {{.*}} @_Z6edge_bP5CBase{{.*}} {
// CHECK-EDGES: call void %1{{.*}} !dbg {{![0-9]+}}, !call_target [[CBASE_F1_DCL:![0-9]+]]
// CHECK-EDGES: call void %3{{.*}} !dbg {{![0-9]+}}, !call_target [[CBASE_F2_DCL:![0-9]+]]
// CHECK-EDGES: }

// CHECK-EDGES: define {{.*}} @_Z6edge_cP3CD0{{.*}} {
// CHECK-EDGES: call void %1{{.*}} !dbg {{![0-9]+}}, !call_target [[CD0_D0_DCL:![0-9]+]]
// CHECK-EDGES: call void %4{{.*}} !dbg {{![0-9]+}}, !call_target [[CD0_D1_DCL:![0-9]+]]
// CHECK-EDGES: }

// CHECK-EDGES: [[CD0_D1_DCL]] = {{.*}}!DISubprogram(name: "d1", linkageName: "_ZN3CD03CD12d1Ev", {{.*}}containingType
// CHECK-EDGES: [[CD0_D0_DCL]] = {{.*}}!DISubprogram(name: "d0", linkageName: "_ZN3CD02d0Ev", {{.*}}containingType

// CHECK-EDGES: [[CBASE_F1_DCL]] = {{.*}}!DISubprogram(name: "f1", linkageName: "_ZN5CBase2f1Ev", {{.*}}containingType
// CHECK-EDGES: [[CBASE_F2_DCL]] = {{.*}}!DISubprogram(name: "f2", linkageName: "_ZN5CBase2f2Ev", {{.*}}containingType
// CHECK-EDGES: [[CEMPTY_F2_DEF:![0-9]+]] = {{.*}}!DISubprogram(name: "f2", linkageName: "_ZN6CEmpty2f2Ev", {{.*}}DISPFlagDefinition
// CHECK-EDGES: [[CEMPTY_F2_DCL]] = {{.*}}!DISubprogram(name: "f2", linkageName: "_ZN6CEmpty2f2Ev", {{.*}}containingType
// CHECK-EDGES: [[CEMPTY_F1_DCL]] = {{.*}}!DISubprogram(name: "f1", linkageName: "_ZN6CEmpty2f1Ev", {{.*}}containingType
// CHECK-EDGES: [[CBASE_F2_DEF:![0-9]+]] = {{.*}}!DISubprogram(name: "f2", linkageName: "_ZN5CBase2f2Ev", {{.*}}DISPFlagDefinition

// CHECK-EDGES: [[CD0_D0_DEF:![0-9]+]] = {{.*}}!DISubprogram(name: "d0", linkageName: "_ZN3CD02d0Ev", {{.*}}DISPFlagDefinition
// CHECK-EDGES: [[CD0_D1_DEF:![0-9]+]] = {{.*}}!DISubprogram(name: "d1", linkageName: "_ZN3CD03CD12d1Ev", {{.*}}DISPFlagDefinition
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// RUN: %clang --target=x86_64-unknown-linux -c -g -O1 %s -o - | \
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure if this feature has sufficient cross-project complexity to justify a cross-project-test for it... but shrug don't feel /super/ strongly about it.

Copy link
Member Author

Choose a reason for hiding this comment

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

@felipepiovezan Any thoughts on this? Thanks

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah I'd be more interested in seeing a test case later inside LLDB to make use of this feature.

Copy link
Member Author

Choose a reason for hiding this comment

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

I will keep the test case, but added the following note:

// Note: We should add a test case inside LLDB that make use of the
//       virtuality call-site target information in DWARF.

// RUN: llvm-dwarfdump --debug-info - | FileCheck %s --check-prefix=CHECK

// Simple base and derived class with virtual:
// We check for a generated 'DW_AT_LLVM_virtual_call_origin' for 'foo', that
// corresponds to the 'call_target' metadata added to the indirect call
// instruction.

// Note: We should add a test case inside LLDB that make use of the
// virtuality call-site target information in DWARF.

struct CBaseOne {
virtual void foo(int &);
};

struct CDerivedOne : CBaseOne {
void foo(int &);
};

void CDerivedOne::foo(int &) {}

struct CBaseTwo {
CDerivedOne *DerivedOne;
};

struct CDerivedTwo : CBaseTwo {
void bar(int &);
};

void CDerivedTwo::bar(int &j) { DerivedOne->foo(j); }

// The IR generated looks like:
//
// define dso_local void @_ZN11CDerivedTwo3barERi(...) !dbg !40 {
// entry:
// ..
// %vtable = load ptr, ptr %0, align 8
// %vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
// %2 = load ptr, ptr %vfn, align 8
// call void %2(...), !dbg !65, !call_target !25
// ret void
// }
//
// !25 = !DISubprogram(name: "foo", linkageName: "_ZN11CDerivedOne3fooERi", ...)
// !40 = !DISubprogram(name: "bar", linkageName: "_ZN11CDerivedTwo3barERi", ...)
// !65 = !DILocation(line: 25, column: 15, scope: !40)

// CHECK: DW_TAG_compile_unit
// CHECK: DW_TAG_structure_type
// CHECK: DW_AT_name ("CDerivedOne")
// CHECK: [[FOO_DCL:0x[a-f0-9]+]]: DW_TAG_subprogram
// CHECK: DW_AT_name ("foo")
// CHECK: DW_TAG_structure_type
// CHECK: DW_AT_name ("CBaseOne")
// CHECK: [[FOO_DEF:0x[a-f0-9]+]]: DW_TAG_subprogram
// CHECK: DW_AT_call_all_calls (true)
// CHECK: DW_AT_specification ([[FOO_DCL]] "{{.*}}foo{{.*}}")
// CHECK: DW_TAG_structure_type
// CHECK: DW_AT_name ("CDerivedTwo")
// CHECK: DW_TAG_subprogram
// CHECK: DW_AT_name ("bar")
// CHECK: DW_TAG_structure_type
// CHECK: DW_AT_name ("CBaseTwo")
// CHECK: DW_TAG_subprogram
// CHECK: DW_AT_call_all_calls (true)
// CHECK: DW_AT_specification (0x{{.*}} "{{.*}}bar{{.*}}")
// CHECK: DW_TAG_call_site
// CHECK: DW_AT_call_target_clobbered (DW_OP_reg0 RAX)
// CHECK: DW_AT_call_tail_call (true)
// CHECK: DW_AT_call_pc (0x{{.*}})
// CHECK: DW_AT_LLVM_virtual_call_origin ([[FOO_DCL]] "{{.*}}foo{{.*}}")
1 change: 1 addition & 0 deletions llvm/include/llvm/BinaryFormat/Dwarf.def
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ HANDLE_DW_AT(0x3e10, LLVM_address_space, 0, LLVM)
HANDLE_DW_AT(0x3e11, LLVM_lanes, 0, LLVM)
HANDLE_DW_AT(0x3e12, LLVM_lane_pc, 0, LLVM)
HANDLE_DW_AT(0x3e13, LLVM_vector_size, 0, LLVM)
HANDLE_DW_AT(0x3e14, LLVM_virtual_call_origin, 0, LLVM)

// https://llvm.org/docs/AMDGPUUsage.html#address-space-identifier
HANDLE_DW_ASPACE(0x0, none)
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/CodeGen/MachineFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,11 +526,17 @@ class LLVM_ABI MachineFunction {
/// Callee type ids.
SmallVector<ConstantInt *, 4> CalleeTypeIds;

/// 'call_target' metadata for the DISubprogram. It is the declaration
/// or definition of the target function and might be indirect.
MDNode *CallTarget = nullptr;

CallSiteInfo() = default;

/// Extracts the numeric type id from the CallBase's callee_type Metadata,
/// and sets CalleeTypeIds. This is used as type id for the indirect call in
/// the call graph section.
/// Extracts the MDNode from the CallBase's call_target Metadata to be used
/// during the construction of the debug info call site entries.
LLVM_ABI CallSiteInfo(const CallBase &CB);
};

Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/CodeGen/TargetLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -5930,6 +5930,10 @@ class LLVM_ABI TargetLowering : public TargetLoweringBase {
LoadSDNode *OriginalLoad,
SelectionDAG &DAG) const;

protected:
void setTypeIdForCallsiteInfo(const CallBase *CB, MachineFunction &MF,
MachineFunction::CallSiteInfo &CSInfo) const;

private:
SDValue foldSetCCWithAnd(EVT VT, SDValue N0, SDValue N1, ISD::CondCode Cond,
const SDLoc &DL, DAGCombinerInfo &DCI) const;
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/FixedMetadataKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ LLVM_FIXED_MD_KIND(MD_captures, "captures", 44)
LLVM_FIXED_MD_KIND(MD_alloc_token, "alloc_token", 45)
LLVM_FIXED_MD_KIND(MD_implicit_ref, "implicit.ref", 46)
LLVM_FIXED_MD_KIND(MD_nofpclass, "nofpclass", 47)
LLVM_FIXED_MD_KIND(MD_call_target, "call_target", 48)
15 changes: 10 additions & 5 deletions llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1349,11 +1349,7 @@ DIE &DwarfCompileUnit::constructCallSiteEntryDIE(
} else if (CalleeSP) {
DIE *CalleeDIE = getOrCreateSubprogramDIE(CalleeSP, CalleeF);
assert(CalleeDIE && "Could not create DIE for call site entry origin");
if (AddLinkageNamesToDeclCallOriginsForTuning(DD) &&
!CalleeSP->isDefinition() &&
!CalleeDIE->findAttribute(dwarf::DW_AT_linkage_name)) {
addLinkageName(*CalleeDIE, CalleeSP->getLinkageName());
}
addLinkageNamesToDeclarations(*DD, *CalleeSP, *CalleeDIE);

addDIEEntry(CallSiteDIE, getDwarf5OrGNUAttr(dwarf::DW_AT_call_origin),
*CalleeDIE);
Expand Down Expand Up @@ -1891,3 +1887,12 @@ DIE *DwarfCompileUnit::getOrCreateSubprogramDIE(const DISubprogram *SP,

return DwarfUnit::getOrCreateSubprogramDIE(SP, F, Minimal);
}

void DwarfCompileUnit::addLinkageNamesToDeclarations(
const DwarfDebug &DD, const DISubprogram &CalleeSP, DIE &CalleeDIE) {
if (AddLinkageNamesToDeclCallOriginsForTuning(&DD) &&
!CalleeSP.isDefinition() &&
!CalleeDIE.findAttribute(dwarf::DW_AT_linkage_name)) {
addLinkageName(CalleeDIE, CalleeSP.getLinkageName());
}
}
Loading