Skip to content

Commit fa1dceb

Browse files
authored
[DebugInfo][DWARF] Allow memory locations in DW_AT_call_target expressions (llvm#171183)
Fixes llvm#70949. Prior to PR llvm#151378 memory locations were incorrect; that patch prevented the emission of the incorrect locations. This patch fixes the underlying issue.
1 parent f721a39 commit fa1dceb

File tree

5 files changed

+174
-38
lines changed

5 files changed

+174
-38
lines changed

llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,15 +1321,22 @@ DwarfCompileUnit::getDwarf5OrGNULocationAtom(dwarf::LocationAtom Loc) const {
13211321
DIE &DwarfCompileUnit::constructCallSiteEntryDIE(
13221322
DIE &ScopeDIE, const DISubprogram *CalleeSP, const Function *CalleeF,
13231323
bool IsTail, const MCSymbol *PCAddr, const MCSymbol *CallAddr,
1324-
unsigned CallReg, DIType *AllocSiteTy) {
1324+
MachineLocation CallTarget, int64_t Offset, DIType *AllocSiteTy) {
13251325
// Insert a call site entry DIE within ScopeDIE.
13261326
DIE &CallSiteDIE = createAndAddDIE(getDwarf5OrGNUTag(dwarf::DW_TAG_call_site),
13271327
ScopeDIE, nullptr);
13281328

1329-
if (CallReg) {
1330-
// Indirect call.
1331-
addAddress(CallSiteDIE, getDwarf5OrGNUAttr(dwarf::DW_AT_call_target),
1332-
MachineLocation(CallReg));
1329+
// A valid register in CallTarget indicates an indirect call.
1330+
if (CallTarget.getReg()) {
1331+
// CallTarget is the location of the address of an indirect call. The
1332+
// location may be indirect, modified by Offset.
1333+
if (CallTarget.isIndirect())
1334+
addMemoryLocation(CallSiteDIE,
1335+
getDwarf5OrGNUAttr(dwarf::DW_AT_call_target),
1336+
CallTarget, Offset);
1337+
else
1338+
addAddress(CallSiteDIE, getDwarf5OrGNUAttr(dwarf::DW_AT_call_target),
1339+
CallTarget);
13331340
} else if (CalleeSP) {
13341341
DIE *CalleeDIE = getOrCreateSubprogramDIE(CalleeSP, CalleeF);
13351342
assert(CalleeDIE && "Could not create DIE for call site entry origin");
@@ -1642,15 +1649,15 @@ void DwarfCompileUnit::addVariableAddress(const DbgVariable &DV, DIE &Die,
16421649
addAddress(Die, dwarf::DW_AT_location, Location);
16431650
}
16441651

1645-
/// Add an address attribute to a die based on the location provided.
1646-
void DwarfCompileUnit::addAddress(DIE &Die, dwarf::Attribute Attribute,
1647-
const MachineLocation &Location) {
1652+
void DwarfCompileUnit::addLocationWithExpr(DIE &Die, dwarf::Attribute Attribute,
1653+
const MachineLocation &Location,
1654+
ArrayRef<uint64_t> Expr) {
16481655
DIELoc *Loc = new (DIEValueAllocator) DIELoc;
16491656
DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc);
16501657
if (Location.isIndirect())
16511658
DwarfExpr.setMemoryLocationKind();
16521659

1653-
DIExpressionCursor Cursor({});
1660+
DIExpressionCursor Cursor(Expr);
16541661
const TargetRegisterInfo &TRI = *Asm->MF->getSubtarget().getRegisterInfo();
16551662
if (!DwarfExpr.addMachineRegExpression(TRI, Cursor, Location.getReg()))
16561663
return;
@@ -1664,6 +1671,23 @@ void DwarfCompileUnit::addAddress(DIE &Die, dwarf::Attribute Attribute,
16641671
*DwarfExpr.TagOffset);
16651672
}
16661673

1674+
/// Add an address attribute to a die based on the location provided.
1675+
void DwarfCompileUnit::addAddress(DIE &Die, dwarf::Attribute Attribute,
1676+
const MachineLocation &Location) {
1677+
addLocationWithExpr(Die, Attribute, Location, {});
1678+
}
1679+
1680+
/// Add a memory location exprloc to \p DIE with attribute \p Attribute
1681+
/// at \p Location + \p Offset.
1682+
void DwarfCompileUnit::addMemoryLocation(DIE &Die, dwarf::Attribute Attribute,
1683+
const MachineLocation &Location,
1684+
int64_t Offset) {
1685+
assert(Location.isIndirect() && "Memory loc should be indirect");
1686+
SmallVector<uint64_t, 3> Ops;
1687+
DIExpression::appendOffset(Ops, Offset);
1688+
addLocationWithExpr(Die, Attribute, Location, Ops);
1689+
}
1690+
16671691
/// Start with the address based on the location provided, and generate the
16681692
/// DWARF information necessary to find the actual variable given the extra
16691693
/// address information encoded in the DbgVariable, starting from the starting

llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ class DwarfCompileUnit final : public DwarfUnit {
160160
DIE &createAbstractSubprogramDIE(const DISubprogram *SP, DIE *ContextDIE,
161161
DwarfCompileUnit *ContextCU);
162162

163+
/// Add a location exprloc to \p DIE with attribute \p Attribute at
164+
/// for \p Location modified by raw DIExpression \p Expr.
165+
void addLocationWithExpr(DIE &Die, dwarf::Attribute Attribute,
166+
const MachineLocation &Location,
167+
ArrayRef<uint64_t> Expr);
168+
163169
public:
164170
DwarfCompileUnit(unsigned UID, const DICompileUnit *Node, AsmPrinter *A,
165171
DwarfDebug *DW, DwarfFile *DWU,
@@ -309,12 +315,14 @@ class DwarfCompileUnit final : public DwarfUnit {
309315
/// \p IsTail specifies whether the call is a tail call.
310316
/// \p PCAddr points to the PC value after the call instruction.
311317
/// \p CallAddr points to the PC value at the call instruction (or is null).
312-
/// \p CallReg is a register location for an indirect call. For direct calls
313-
/// the \p CallReg is set to 0.
318+
/// \p CallTarget a location holding the target address for an indirect call.
319+
/// For direct calls \p CallTarget register is set to 0.
320+
/// \p Offset from \p CallTarget register value if the location is indirect.
314321
DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, const DISubprogram *CalleeSP,
315322
const Function *CalleeF, bool IsTail,
316323
const MCSymbol *PCAddr,
317-
const MCSymbol *CallAddr, unsigned CallReg,
324+
const MCSymbol *CallAddr,
325+
MachineLocation CallTarget, int64_t Offset,
318326
DIType *AllocSiteTy);
319327
/// Construct call site parameter DIEs for the \p CallSiteDIE. The \p Params
320328
/// were collected by the \ref collectCallSiteParameters.
@@ -385,6 +393,10 @@ class DwarfCompileUnit final : public DwarfUnit {
385393
void addAddress(DIE &Die, dwarf::Attribute Attribute,
386394
const MachineLocation &Location);
387395

396+
/// Add a memory location exprloc to \p DIE with attribute \p Attribute
397+
/// at \p Location + \p Offset.
398+
void addMemoryLocation(DIE &Die, dwarf::Attribute Attribute,
399+
const MachineLocation &Location, int64_t Offset);
388400
/// Start with the address based on the location provided, and generate the
389401
/// DWARF information necessary to find the actual variable (navigating the
390402
/// extra location information encoded in the type) based on the starting

llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -942,25 +942,27 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
942942
DIType *AllocSiteTy = dyn_cast_or_null<DIType>(MI.getHeapAllocMarker());
943943

944944
// If this is a direct call, find the callee's subprogram.
945-
// In the case of an indirect call find the register that holds
946-
// the callee.
945+
// In the case of an indirect call find the register or memory location
946+
// that holds the callee address.
947947
const MachineOperand &CalleeOp = TII->getCalleeOperand(MI);
948948
bool PhysRegCalleeOperand =
949949
CalleeOp.isReg() && CalleeOp.getReg().isPhysical();
950-
// Hack: WebAssembly CALL instructions have MCInstrDesc that does not
951-
// describe the call target operand.
952-
if (CalleeOp.getOperandNo() < MI.getDesc().operands().size()) {
953-
const MCOperandInfo &MCOI =
954-
MI.getDesc().operands()[CalleeOp.getOperandNo()];
955-
PhysRegCalleeOperand =
956-
PhysRegCalleeOperand && MCOI.OperandType == MCOI::OPERAND_REGISTER;
957-
}
958-
959-
unsigned CallReg = 0;
950+
MachineLocation CallTarget{0};
951+
int64_t Offset = 0;
960952
const DISubprogram *CalleeSP = nullptr;
961953
const Function *CalleeDecl = nullptr;
962954
if (PhysRegCalleeOperand) {
963-
CallReg = CalleeOp.getReg(); // might be zero
955+
bool Scalable = false;
956+
const MachineOperand *BaseOp = nullptr;
957+
const TargetRegisterInfo &TRI =
958+
*Asm->MF->getSubtarget().getRegisterInfo();
959+
if (TII->getMemOperandWithOffset(MI, BaseOp, Offset, Scalable, &TRI)) {
960+
if (BaseOp && BaseOp->isReg() && !Scalable)
961+
CallTarget = MachineLocation(BaseOp->getReg(), /*Indirect*/ true);
962+
}
963+
964+
if (!CallTarget.isIndirect())
965+
CallTarget = MachineLocation(CalleeOp.getReg()); // Might be zero.
964966
} else if (CalleeOp.isGlobal()) {
965967
CalleeDecl = dyn_cast<Function>(CalleeOp.getGlobal());
966968
if (CalleeDecl)
@@ -969,7 +971,8 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
969971

970972
// Omit DIE if we can't tell where the call goes *and* we don't want to
971973
// add metadata to it.
972-
if (CalleeSP == nullptr && CallReg == 0 && AllocSiteTy == nullptr)
974+
if (CalleeSP == nullptr && CallTarget.getReg() == 0 &&
975+
AllocSiteTy == nullptr)
973976
continue;
974977

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

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

1000-
LLVM_DEBUG(dbgs() << "CallSiteEntry: " << MF.getName() << " -> "
1001-
<< (CalleeDecl ? CalleeDecl->getName()
1002-
: StringRef(MF.getSubtarget()
1003-
.getRegisterInfo()
1004-
->getName(CallReg)))
1005-
<< (IsTail ? " [IsTail]" : "") << "\n");
1006-
1007-
DIE &CallSiteDIE =
1008-
CU.constructCallSiteEntryDIE(ScopeDIE, CalleeSP, CalleeDecl, IsTail,
1009-
PCAddr, CallAddr, CallReg, AllocSiteTy);
1003+
LLVM_DEBUG(
1004+
dbgs() << "CallSiteEntry: " << MF.getName() << " -> "
1005+
<< (CalleeDecl
1006+
? CalleeDecl->getName()
1007+
: StringRef(
1008+
MF.getSubtarget().getRegisterInfo()->getName(
1009+
CallTarget.getReg())))
1010+
<< (IsTail ? " [IsTail]" : "") << "\n");
1011+
1012+
DIE &CallSiteDIE = CU.constructCallSiteEntryDIE(
1013+
ScopeDIE, CalleeSP, CalleeDecl, IsTail, PCAddr, CallAddr, CallTarget,
1014+
Offset, AllocSiteTy);
10101015

10111016
// Optionally emit call-site-param debug info.
10121017
if (emitDebugEntryValues()) {
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# RUN: llc %s --start-after=livedebugvalues -o - --filetype=obj | llvm-dwarfdump - | FileCheck %s
2+
3+
## Check the memory location of the target address for the indirect call
4+
## (virtual in this case) is described by a DW_AT_call_target expression.
5+
6+
# CHECK: DW_TAG_call_site
7+
# CHECK-NEXT: DW_AT_call_target (DW_OP_breg0 RAX+8)
8+
9+
## Generated from this C++ with llc -stop-after=livedebugvalues -simplify-mir:
10+
## struct Base {
11+
## virtual int zz() { return x; }
12+
## [[clang::noinline]]
13+
## virtual int v() { return zz(); }
14+
## int x;
15+
## };
16+
## struct Child: public Base {
17+
## [[clang::noinline]]
18+
## virtual int v() { return x * 2; }
19+
## int x;
20+
## };
21+
##
22+
## [[clang::noinline]]
23+
## [[clang::disable_tail_calls]]
24+
## int foo(Base* b) {
25+
## return b->v();
26+
## }
27+
28+
--- |
29+
target triple = "x86_64-unknown-linux-gnu"
30+
31+
define dso_local noundef i32 @_Z3fooP4Base(ptr noundef %b) local_unnamed_addr !dbg !5 {
32+
entry:
33+
%vtable = load ptr, ptr %b, align 8, !dbg !12
34+
%vfn = getelementptr inbounds nuw i8, ptr %vtable, i64 8, !dbg !12
35+
%0 = load ptr, ptr %vfn, align 8, !dbg !12
36+
%call = call noundef i32 %0(ptr noundef nonnull align 8 dereferenceable(12) %b), !dbg !12
37+
ret i32 %call, !dbg !12
38+
}
39+
40+
!llvm.dbg.cu = !{!0}
41+
!llvm.module.flags = !{!2, !3}
42+
!llvm.ident = !{!4}
43+
44+
!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)
45+
!1 = !DIFile(filename: "test.cpp", directory: "/")
46+
!2 = !{i32 7, !"Dwarf Version", i32 5}
47+
!3 = !{i32 2, !"Debug Info Version", i32 3}
48+
!4 = !{!"clang version 22.0.0git"}
49+
!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)
50+
!6 = !DISubroutineType(types: !7)
51+
!7 = !{!8, !9}
52+
!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
53+
!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64)
54+
!10 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Base", file: !1, line: 1, size: 128, flags: DIFlagFwdDecl | DIFlagNonTrivial, identifier: "_ZTS4Base")
55+
!11 = !{}
56+
!12 = !DILocation(line: 16, scope: !5)
57+
...
58+
---
59+
name: _Z3fooP4Base
60+
alignment: 16
61+
tracksRegLiveness: true
62+
noPhis: true
63+
isSSA: false
64+
noVRegs: true
65+
hasFakeUses: false
66+
debugInstrRef: true
67+
tracksDebugUserValues: true
68+
liveins:
69+
- { reg: '$rdi' }
70+
frameInfo:
71+
stackSize: 8
72+
offsetAdjustment: -8
73+
maxAlignment: 1
74+
adjustsStack: true
75+
hasCalls: true
76+
maxCallFrameSize: 0
77+
isCalleeSavedInfoValid: true
78+
machineFunctionInfo:
79+
amxProgModel: None
80+
body: |
81+
bb.0.entry:
82+
liveins: $rdi
83+
84+
frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp
85+
frame-setup CFI_INSTRUCTION def_cfa_offset 16
86+
renamable $rax = MOV64rm renamable $rdi, 1, $noreg, 0, $noreg, debug-location !12 :: (load (s64) from %ir.b)
87+
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)
88+
$rcx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !12
89+
frame-destroy CFI_INSTRUCTION def_cfa_offset 8, debug-location !12
90+
RET64 $eax, debug-location !12
91+
...

llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs-indirect.ll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
; RUN: llvm-dwarfdump -statistics %t.o | FileCheck %s -check-prefix=STATS
99

1010
; VERIFY: No errors.
11-
; STATS: "#call site DIEs": 1,
11+
; STATS: "#call site DIEs": 2,
1212

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

2020
; OBJ: DW_TAG_call_site
21-
; OBJ: DW_AT_call_target
21+
; OBJ: DW_AT_call_target (DW_OP_reg[[#]] {{.*}})
2222
; OBJ: DW_AT_call_return_pc
2323
call void (...) %f() #1, !dbg !19
2424
ret void, !dbg !20
@@ -31,6 +31,10 @@ define dso_local void @call_mem(ptr noundef readonly captures(none) %f) local_un
3131
entry:
3232
#dbg_value(ptr %f, !26, !DIExpression(), !27)
3333
%0 = load ptr, ptr %f, align 8, !dbg !28, !tbaa !29
34+
35+
; OBJ: DW_TAG_call_site
36+
; OBJ: DW_AT_call_target (DW_OP_breg[[#]] {{.*}})
37+
; OBJ: DW_AT_call_return_pc
3438
call void (...) %0() #1, !dbg !28
3539
ret void, !dbg !33
3640
}

0 commit comments

Comments
 (0)