[llvm] [DebugInfo][DWARF] Allow memory locations in DW_AT_call_target expressions (PR #171183)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Dec 8 10:36:18 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-debuginfo
Author: Orlando Cazalet-Hyams (OCHyams)
<details>
<summary>Changes</summary>
Fixes #<!-- -->70949. Prior to PR #<!-- -->151378 memory locations were incorrect; that patch prevented the emission of the incorrect locations. This patch fixes the underlying issue.
---
Patch to start using DW_AT_call_target_clobbered to follow once this is merged.
---
Full diff: https://github.com/llvm/llvm-project/pull/171183.diff
5 Files Affected:
- (modified) llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp (+31-9)
- (modified) llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h (+15-3)
- (modified) llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp (+30-24)
- (added) llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir (+91)
- (modified) llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs-indirect.ll (+6-2)
``````````diff
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 751d3735d3b2b..a8d3e84cf43c0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -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");
@@ -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;
@@ -1662,6 +1669,21 @@ 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, {});
+}
+
+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
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
index a3bbc8364599d..a90fec0da0837 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
@@ -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,
@@ -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.
@@ -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
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 567acf75d1b8d..a86fe3009e98d 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -942,25 +942,28 @@ 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)
@@ -969,7 +972,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).
@@ -997,16 +1001,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()) {
diff --git a/llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir b/llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir
new file mode 100644
index 0000000000000..ef8a080cebaae
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir
@@ -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
+...
diff --git a/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs-indirect.ll b/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs-indirect.ll
index 6c81e2e72d93c..f64b78f5820e4 100644
--- a/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs-indirect.ll
+++ b/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs-indirect.ll
@@ -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")
@@ -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
@@ -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
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/171183
More information about the llvm-commits
mailing list