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
42 changes: 33 additions & 9 deletions llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1319,15 +1319,22 @@ DwarfCompileUnit::getDwarf5OrGNULocationAtom(dwarf::LocationAtom Loc) const {
DIE &DwarfCompileUnit::constructCallSiteEntryDIE(
DIE &ScopeDIE, const DISubprogram *CalleeSP, const Function *CalleeF,
bool IsTail, const MCSymbol *PCAddr, const MCSymbol *CallAddr,
unsigned CallReg, DIType *AllocSiteTy) {
MachineLocation CallTarget, int64_t Offset, DIType *AllocSiteTy) {
// Insert a call site entry DIE within ScopeDIE.
DIE &CallSiteDIE = createAndAddDIE(getDwarf5OrGNUTag(dwarf::DW_TAG_call_site),
ScopeDIE, nullptr);

if (CallReg) {
// Indirect call.
addAddress(CallSiteDIE, getDwarf5OrGNUAttr(dwarf::DW_AT_call_target),
MachineLocation(CallReg));
// A valid register in CallTarget indicates an indirect call.
if (CallTarget.getReg()) {
// CallTarget is the location of the address of an indirect call. The
// location may be indirect, modified by Offset.
if (CallTarget.isIndirect())
addMemoryLocation(CallSiteDIE,
getDwarf5OrGNUAttr(dwarf::DW_AT_call_target),
CallTarget, Offset);
else
addAddress(CallSiteDIE, getDwarf5OrGNUAttr(dwarf::DW_AT_call_target),
CallTarget);
} else if (CalleeSP) {
DIE *CalleeDIE = getOrCreateSubprogramDIE(CalleeSP, CalleeF);
assert(CalleeDIE && "Could not create DIE for call site entry origin");
Expand Down Expand Up @@ -1640,15 +1647,15 @@ void DwarfCompileUnit::addVariableAddress(const DbgVariable &DV, DIE &Die,
addAddress(Die, dwarf::DW_AT_location, Location);
}

/// Add an address attribute to a die based on the location provided.
void DwarfCompileUnit::addAddress(DIE &Die, dwarf::Attribute Attribute,
const MachineLocation &Location) {
void DwarfCompileUnit::addLocationWithExpr(DIE &Die, dwarf::Attribute Attribute,
const MachineLocation &Location,
ArrayRef<uint64_t> Expr) {
DIELoc *Loc = new (DIEValueAllocator) DIELoc;
DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc);
if (Location.isIndirect())
DwarfExpr.setMemoryLocationKind();

DIExpressionCursor Cursor({});
DIExpressionCursor Cursor(Expr);
const TargetRegisterInfo &TRI = *Asm->MF->getSubtarget().getRegisterInfo();
if (!DwarfExpr.addMachineRegExpression(TRI, Cursor, Location.getReg()))
return;
Expand All @@ -1662,6 +1669,23 @@ void DwarfCompileUnit::addAddress(DIE &Die, dwarf::Attribute Attribute,
*DwarfExpr.TagOffset);
}

/// Add an address attribute to a die based on the location provided.
void DwarfCompileUnit::addAddress(DIE &Die, dwarf::Attribute Attribute,
const MachineLocation &Location) {
addLocationWithExpr(Die, Attribute, Location, {});
}

/// Add a memory location exprloc to \p DIE with attribute \p Attribute
/// at \p Location + \p Offset.
void DwarfCompileUnit::addMemoryLocation(DIE &Die, dwarf::Attribute Attribute,
const MachineLocation &Location,
int64_t Offset) {
assert(Location.isIndirect() && "Memory loc should be indirect");
SmallVector<uint64_t, 3> Ops;
DIExpression::appendOffset(Ops, Offset);
addLocationWithExpr(Die, Attribute, Location, Ops);
}

/// Start with the address based on the location provided, and generate the
/// DWARF information necessary to find the actual variable given the extra
/// address information encoded in the DbgVariable, starting from the starting
Expand Down
18 changes: 15 additions & 3 deletions llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ class DwarfCompileUnit final : public DwarfUnit {
DIE &createAbstractSubprogramDIE(const DISubprogram *SP, DIE *ContextDIE,
DwarfCompileUnit *ContextCU);

/// Add a location exprloc to \p DIE with attribute \p Attribute at
/// for \p Location modified by raw DIExpression \p Expr.
void addLocationWithExpr(DIE &Die, dwarf::Attribute Attribute,
const MachineLocation &Location,
ArrayRef<uint64_t> Expr);

public:
DwarfCompileUnit(unsigned UID, const DICompileUnit *Node, AsmPrinter *A,
DwarfDebug *DW, DwarfFile *DWU,
Expand Down Expand Up @@ -309,12 +315,14 @@ class DwarfCompileUnit final : public DwarfUnit {
/// \p IsTail specifies whether the call is a tail call.
/// \p PCAddr points to the PC value after the call instruction.
/// \p CallAddr points to the PC value at the call instruction (or is null).
/// \p CallReg is a register location for an indirect call. For direct calls
/// the \p CallReg is set to 0.
/// \p CallTarget a location holding the target address for an indirect call.
/// For direct calls \p CallTarget register is set to 0.
/// \p Offset from \p CallTarget register value if the location is indirect.
DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, const DISubprogram *CalleeSP,
const Function *CalleeF, bool IsTail,
const MCSymbol *PCAddr,
const MCSymbol *CallAddr, unsigned CallReg,
const MCSymbol *CallAddr,
MachineLocation CallTarget, int64_t Offset,
DIType *AllocSiteTy);
/// Construct call site parameter DIEs for the \p CallSiteDIE. The \p Params
/// were collected by the \ref collectCallSiteParameters.
Expand Down Expand Up @@ -385,6 +393,10 @@ class DwarfCompileUnit final : public DwarfUnit {
void addAddress(DIE &Die, dwarf::Attribute Attribute,
const MachineLocation &Location);

/// Add a memory location exprloc to \p DIE with attribute \p Attribute
/// at \p Location + \p Offset.
void addMemoryLocation(DIE &Die, dwarf::Attribute Attribute,
const MachineLocation &Location, int64_t Offset);
/// Start with the address based on the location provided, and generate the
/// DWARF information necessary to find the actual variable (navigating the
/// extra location information encoded in the type) based on the starting
Expand Down
53 changes: 29 additions & 24 deletions llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -942,25 +942,27 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
DIType *AllocSiteTy = dyn_cast_or_null<DIType>(MI.getHeapAllocMarker());

// If this is a direct call, find the callee's subprogram.
// In the case of an indirect call find the register that holds
// the callee.
// In the case of an indirect call find the register or memory location
// that holds the callee address.
const MachineOperand &CalleeOp = TII->getCalleeOperand(MI);
bool PhysRegCalleeOperand =
CalleeOp.isReg() && CalleeOp.getReg().isPhysical();
// Hack: WebAssembly CALL instructions have MCInstrDesc that does not
// describe the call target operand.
if (CalleeOp.getOperandNo() < MI.getDesc().operands().size()) {
const MCOperandInfo &MCOI =
MI.getDesc().operands()[CalleeOp.getOperandNo()];
PhysRegCalleeOperand =
PhysRegCalleeOperand && MCOI.OperandType == MCOI::OPERAND_REGISTER;
}

unsigned CallReg = 0;
MachineLocation CallTarget{0};
int64_t Offset = 0;
const DISubprogram *CalleeSP = nullptr;
const Function *CalleeDecl = nullptr;
if (PhysRegCalleeOperand) {
CallReg = CalleeOp.getReg(); // might be zero
bool Scalable = false;
const MachineOperand *BaseOp = nullptr;
const TargetRegisterInfo &TRI =
*Asm->MF->getSubtarget().getRegisterInfo();
if (TII->getMemOperandWithOffset(MI, BaseOp, Offset, Scalable, &TRI)) {
if (BaseOp && BaseOp->isReg() && !Scalable)
CallTarget = MachineLocation(BaseOp->getReg(), /*Indirect*/ true);
}

if (!CallTarget.isIndirect())
CallTarget = MachineLocation(CalleeOp.getReg()); // Might be zero.
} else if (CalleeOp.isGlobal()) {
CalleeDecl = dyn_cast<Function>(CalleeOp.getGlobal());
if (CalleeDecl)
Expand All @@ -969,7 +971,8 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,

// Omit DIE if we can't tell where the call goes *and* we don't want to
// add metadata to it.
if (CalleeSP == nullptr && CallReg == 0 && AllocSiteTy == nullptr)
if (CalleeSP == nullptr && CallTarget.getReg() == 0 &&
AllocSiteTy == nullptr)
continue;

// TODO: Omit call site entries for runtime calls (objc_msgSend, etc).
Expand Down Expand Up @@ -997,16 +1000,18 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,

assert((IsTail || PCAddr) && "Non-tail call without return PC");

LLVM_DEBUG(dbgs() << "CallSiteEntry: " << MF.getName() << " -> "
<< (CalleeDecl ? CalleeDecl->getName()
: StringRef(MF.getSubtarget()
.getRegisterInfo()
->getName(CallReg)))
<< (IsTail ? " [IsTail]" : "") << "\n");

DIE &CallSiteDIE =
CU.constructCallSiteEntryDIE(ScopeDIE, CalleeSP, CalleeDecl, IsTail,
PCAddr, CallAddr, CallReg, AllocSiteTy);
LLVM_DEBUG(
dbgs() << "CallSiteEntry: " << MF.getName() << " -> "
<< (CalleeDecl
? CalleeDecl->getName()
: StringRef(
MF.getSubtarget().getRegisterInfo()->getName(
CallTarget.getReg())))
<< (IsTail ? " [IsTail]" : "") << "\n");

DIE &CallSiteDIE = CU.constructCallSiteEntryDIE(
ScopeDIE, CalleeSP, CalleeDecl, IsTail, PCAddr, CallAddr, CallTarget,
Offset, AllocSiteTy);

// Optionally emit call-site-param debug info.
if (emitDebugEntryValues()) {
Expand Down
91 changes: 91 additions & 0 deletions llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# RUN: llc %s --start-after=livedebugvalues -o - --filetype=obj | llvm-dwarfdump - | FileCheck %s

## Check the memory location of the target address for the indirect call
## (virtual in this case) is described by a DW_AT_call_target expression.

# CHECK: DW_TAG_call_site
# CHECK-NEXT: DW_AT_call_target (DW_OP_breg0 RAX+8)

## Generated from this C++ with llc -stop-after=livedebugvalues -simplify-mir:
## struct Base {
## virtual int zz() { return x; }
## [[clang::noinline]]
## virtual int v() { return zz(); }
## int x;
## };
## struct Child: public Base {
## [[clang::noinline]]
## virtual int v() { return x * 2; }
## int x;
## };
##
## [[clang::noinline]]
## [[clang::disable_tail_calls]]
## int foo(Base* b) {
## return b->v();
## }

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

define dso_local noundef i32 @_Z3fooP4Base(ptr noundef %b) local_unnamed_addr !dbg !5 {
entry:
%vtable = load ptr, ptr %b, align 8, !dbg !12
%vfn = getelementptr inbounds nuw i8, ptr %vtable, i64 8, !dbg !12
%0 = load ptr, ptr %vfn, align 8, !dbg !12
%call = call noundef i32 %0(ptr noundef nonnull align 8 dereferenceable(12) %b), !dbg !12
ret i32 %call, !dbg !12
}

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!2, !3}
!llvm.ident = !{!4}

!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 22.0.0git", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "test.cpp", directory: "/")
!2 = !{i32 7, !"Dwarf Version", i32 5}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{!"clang version 22.0.0git"}
!5 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooP4Base", scope: !1, file: !1, line: 15, type: !6, scopeLine: 15, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11, keyInstructions: true)
!6 = !DISubroutineType(types: !7)
!7 = !{!8, !9}
!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64)
!10 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Base", file: !1, line: 1, size: 128, flags: DIFlagFwdDecl | DIFlagNonTrivial, identifier: "_ZTS4Base")
!11 = !{}
!12 = !DILocation(line: 16, scope: !5)
...
---
name: _Z3fooP4Base
alignment: 16
tracksRegLiveness: true
noPhis: true
isSSA: false
noVRegs: true
hasFakeUses: false
debugInstrRef: true
tracksDebugUserValues: true
liveins:
- { reg: '$rdi' }
frameInfo:
stackSize: 8
offsetAdjustment: -8
maxAlignment: 1
adjustsStack: true
hasCalls: true
maxCallFrameSize: 0
isCalleeSavedInfoValid: true
machineFunctionInfo:
amxProgModel: None
body: |
bb.0.entry:
liveins: $rdi

frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp
frame-setup CFI_INSTRUCTION def_cfa_offset 16
renamable $rax = MOV64rm renamable $rdi, 1, $noreg, 0, $noreg, debug-location !12 :: (load (s64) from %ir.b)
CALL64m killed renamable $rax, 1, $noreg, 8, $noreg, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !12 :: (load (s64) from %ir.vfn)
$rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !12
frame-destroy CFI_INSTRUCTION def_cfa_offset 8, debug-location !12
RET64 $eax, debug-location !12
...
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
; RUN: llvm-dwarfdump -statistics %t.o | FileCheck %s -check-prefix=STATS

; VERIFY: No errors.
; STATS: "#call site DIEs": 1,
; STATS: "#call site DIEs": 2,

; OBJ: DW_TAG_subprogram
; OBJ: DW_AT_name ("call_reg")
Expand All @@ -18,7 +18,7 @@ entry:
#dbg_value(ptr %f, !17, !DIExpression(), !18)

; OBJ: DW_TAG_call_site
; OBJ: DW_AT_call_target
; OBJ: DW_AT_call_target (DW_OP_reg[[#]] {{.*}})
; OBJ: DW_AT_call_return_pc
call void (...) %f() #1, !dbg !19
ret void, !dbg !20
Expand All @@ -31,6 +31,10 @@ define dso_local void @call_mem(ptr noundef readonly captures(none) %f) local_un
entry:
#dbg_value(ptr %f, !26, !DIExpression(), !27)
%0 = load ptr, ptr %f, align 8, !dbg !28, !tbaa !29

; OBJ: DW_TAG_call_site
; OBJ: DW_AT_call_target (DW_OP_breg[[#]] {{.*}})
; OBJ: DW_AT_call_return_pc
call void (...) %0() #1, !dbg !28
ret void, !dbg !33
}
Expand Down