[DebugInfo][DWARF] Allow memory locations in DW_AT_call_target expressions (#171183)
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.
This commit is contained in:
parent
f721a3965c
commit
fa1dceb67f
@ -1321,15 +1321,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");
|
||||
@ -1642,15 +1649,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;
|
||||
@ -1664,6 +1671,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
|
||||
|
||||
@ -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 @@ public:
|
||||
/// \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 @@ public:
|
||||
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
|
||||
|
||||
@ -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)
|
||||
@ -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).
|
||||
@ -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");
|
||||
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, CallReg, AllocSiteTy);
|
||||
DIE &CallSiteDIE = CU.constructCallSiteEntryDIE(
|
||||
ScopeDIE, CalleeSP, CalleeDecl, IsTail, PCAddr, CallAddr, CallTarget,
|
||||
Offset, AllocSiteTy);
|
||||
|
||||
// Optionally emit call-site-param debug info.
|
||||
if (emitDebugEntryValues()) {
|
||||
|
||||
91
llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir
Normal file
91
llvm/test/DebugInfo/X86/dwarf-call-target-mem-loc.mir
Normal file
@ -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
|
||||
...
|
||||
@ -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
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user