Skip to content

Commit

Permalink
[DwarfExpression] Support entry values for indirect parameters
Browse files Browse the repository at this point in the history
Summary:
A struct argument can be passed-by-value to a callee via a pointer to a
temporary stack copy. Add support for emitting an entry value DBG_VALUE
when an indirect parameter DBG_VALUE becomes unavailable. This is done
by omitting DW_OP_stack_value from the entry value expression, to make
the expression describe the location of an object.

rdar://63373691

Reviewers: djtodoro, aprantl, dstenb

Subscribers: hiraditya, lldb-commits, llvm-commits

Tags: #lldb, #llvm

Differential Revision: https://reviews.llvm.org/D80345
  • Loading branch information
vedantk committed May 26, 2020
1 parent 62eba3f commit 15919fb
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 25 deletions.
8 changes: 5 additions & 3 deletions llvm/docs/LangRef.rst
Expand Up @@ -4823,9 +4823,11 @@ The current supported opcode vocabulary is limited:

``DW_OP_LLVM_entry_value`` is only legal in MIR. The operation is introduced
by the ``LiveDebugValues`` pass; currently only for function parameters that
are unmodified throughout a function and that are described as simple
register location descriptions. The operation is also introduced by the
``AsmPrinter`` pass when a call site parameter value
are unmodified throughout a function. Support is limited to function
parameter that are described as simple register location descriptions, or as
indirect locations (e.g. when a struct is passed-by-value to a callee via a
pointer to a temporary copy made in the caller). The entry value op is also
introduced by the ``AsmPrinter`` pass when a call site parameter value
(``DW_AT_call_site_parameter_value``) is represented as entry value of the
parameter.
- ``DW_OP_breg`` (or ``DW_OP_bregx``) represents a content on the provided
Expand Down
7 changes: 2 additions & 5 deletions llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
Expand Up @@ -1238,15 +1238,12 @@ void DwarfCompileUnit::addComplexAddress(const DbgVariable &DV, DIE &Die,
DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc);
const DIExpression *DIExpr = DV.getSingleExpression();
DwarfExpr.addFragmentOffset(DIExpr);
if (Location.isIndirect())
DwarfExpr.setMemoryLocationKind();
DwarfExpr.setLocation(Location, DIExpr);

DIExpressionCursor Cursor(DIExpr);

if (DIExpr->isEntryValue()) {
DwarfExpr.setEntryValueFlag();
if (DIExpr->isEntryValue())
DwarfExpr.beginEntryValueExpression(Cursor);
}

const TargetRegisterInfo &TRI = *Asm->MF->getSubtarget().getRegisterInfo();
if (!DwarfExpr.addMachineRegExpression(TRI, Cursor, Location.getReg()))
Expand Down
7 changes: 2 additions & 5 deletions llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
Expand Up @@ -2379,14 +2379,11 @@ void DwarfDebug::emitDebugLocValue(const AsmPrinter &AP, const DIBasicType *BT,
DwarfExpr.addUnsignedConstant(Value.getInt());
} else if (Value.isLocation()) {
MachineLocation Location = Value.getLoc();
if (Location.isIndirect())
DwarfExpr.setMemoryLocationKind();
DwarfExpr.setLocation(Location, DIExpr);
DIExpressionCursor Cursor(DIExpr);

if (DIExpr->isEntryValue()) {
DwarfExpr.setEntryValueFlag();
if (DIExpr->isEntryValue())
DwarfExpr.beginEntryValueExpression(Cursor);
}

const TargetRegisterInfo &TRI = *AP.MF->getSubtarget().getRegisterInfo();
if (!DwarfExpr.addMachineRegExpression(TRI, Cursor, Location.getReg()))
Expand Down
22 changes: 21 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
Expand Up @@ -259,7 +259,8 @@ bool DwarfExpression::addMachineRegExpression(const TargetRegisterInfo &TRI,
if (isEntryValue())
finalizeEntryValue();

if (isEntryValue() && !isParameterValue() && DwarfVersion >= 4)
if (isEntryValue() && !isIndirect() && !isParameterValue() &&
DwarfVersion >= 4)
emitOp(dwarf::DW_OP_stack_value);

DwarfRegs.clear();
Expand Down Expand Up @@ -318,6 +319,25 @@ bool DwarfExpression::addMachineRegExpression(const TargetRegisterInfo &TRI,
return true;
}

void DwarfExpression::setEntryValueFlags(const MachineLocation &Loc) {
LocationFlags |= EntryValue;
if (Loc.isIndirect())
LocationFlags |= Indirect;
}

void DwarfExpression::setLocation(const MachineLocation &Loc,
const DIExpression *DIExpr) {
if (Loc.isIndirect())
// Do not treat entry value descriptions of indirect parameters as memory
// locations. This allows DwarfExpression::addReg() to add DW_OP_regN to an
// entry value description.
if (!DIExpr->isEntryValue())
setMemoryLocationKind();

if (DIExpr->isEntryValue())
setEntryValueFlags(Loc);
}

void DwarfExpression::beginEntryValueExpression(
DIExpressionCursor &ExprCursor) {
auto Op = ExprCursor.take();
Expand Down
17 changes: 11 additions & 6 deletions llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
Expand Up @@ -30,6 +30,7 @@ class APInt;
class DwarfCompileUnit;
class DIELoc;
class TargetRegisterInfo;
class MachineLocation;

/// Holds a DIExpression and keeps track of how many operands have been consumed
/// so far.
Expand Down Expand Up @@ -142,14 +143,18 @@ class DwarfExpression {
/// The kind of location description being produced.
enum { Unknown = 0, Register, Memory, Implicit };

/// The flags of location description being produced.
enum { EntryValue = 1, CallSiteParamValue };
/// Additional location flags which may be combined with any location kind.
/// Currently, entry values are not supported for the Memory location kind.
enum { EntryValue = 1 << 0, Indirect = 1 << 1, CallSiteParamValue = 1 << 2 };

unsigned LocationKind : 3;
unsigned LocationFlags : 2;
unsigned LocationFlags : 3;
unsigned DwarfVersion : 4;

public:
/// Set the location (\p Loc) and \ref DIExpression (\p DIExpr) to describe.
void setLocation(const MachineLocation &Loc, const DIExpression *DIExpr);

bool isUnknownLocation() const {
return LocationKind == Unknown;
}
Expand All @@ -174,6 +179,8 @@ class DwarfExpression {
return LocationFlags & CallSiteParamValue;
}

bool isIndirect() const { return LocationFlags & Indirect; }

Optional<uint8_t> TagOffset;

protected:
Expand Down Expand Up @@ -307,9 +314,7 @@ class DwarfExpression {
}

/// Lock this down to become an entry value location.
void setEntryValueFlag() {
LocationFlags |= EntryValue;
}
void setEntryValueFlags(const MachineLocation &Loc);

/// Lock this down to become a call site parameter location.
void setCallSiteParamValueFlag() {
Expand Down
6 changes: 1 addition & 5 deletions llvm/lib/CodeGen/LiveDebugValues.cpp
Expand Up @@ -1612,10 +1612,6 @@ bool LiveDebugValues::isEntryValueCandidate(
if (MI.getDebugLoc()->getInlinedAt())
return false;

// Do not consider indirect debug values (TODO: explain why).
if (MI.isIndirectDebugValue())
return false;

// Only consider parameters that are described using registers. Parameters
// that are passed on the stack are not yet supported, so ignore debug
// values that are described by the frame or stack pointer.
Expand All @@ -1630,7 +1626,7 @@ bool LiveDebugValues::isEntryValueCandidate(
return false;

// TODO: Add support for parameters that have a pre-existing debug expressions
// (e.g. fragments, or indirect parameters using DW_OP_deref).
// (e.g. fragments).
if (MI.getDebugExpression()->getNumElements() > 0)
return false;

Expand Down
@@ -0,0 +1,102 @@
# RUN: llc -start-before=livedebugvalues -stop-after=machineverifier -o - %s \
# RUN: | FileCheck %s -check-prefix=MIR

# Copied from dbgcall-site-indirect-param.mir, with hand modifications:
# an offset is added to the indirect parameter DBG_VALUE.
#
# We do not support emitting an entry value in this case.

# MIR: renamable $w0 = LDRWui killed renamable $x8
# MIR-NOT: DBG_VALUE $x0, 0, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value
# MIR-NEXT: BL @baz
# MIR-NEXT: frame-destroy LDPXpost
# MIR-NEXT: TCRETURNdi @baz

--- |
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
target triple = "arm64-apple-ios10.0.0"

%struct.fat_ptr = type { i32*, i32*, i32* }

define i32 @bar(%struct.fat_ptr* nocapture readonly %f) local_unnamed_addr !dbg !13 {
entry:
call void @llvm.dbg.declare(metadata %struct.fat_ptr* %f, metadata !23, metadata !DIExpression()), !dbg !24
%ptr2 = bitcast %struct.fat_ptr* %f to i32**, !dbg !25
%0 = load i32*, i32** %ptr2, align 8, !dbg !25
%1 = load i32, i32* %0, align 4, !dbg !31
%call = tail call i32 @baz(i32 %1), !dbg !34
%call1 = tail call i32 @baz(i32 %call), !dbg !35
ret i32 %call1, !dbg !36
}

declare void @llvm.dbg.declare(metadata, metadata, metadata)

declare !dbg !4 i32 @baz(i32) local_unnamed_addr optsize

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!8, !9, !10, !11}
!llvm.ident = !{!12}

!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None, sysroot: "/")
!1 = !DIFile(filename: "indirect.c", directory: "/tmp/fatptr")
!2 = !{}
!3 = !{!4}
!4 = !DISubprogram(name: "baz", scope: !1, file: !1, line: 4, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
!5 = !DISubroutineType(types: !6)
!6 = !{!7, !7}
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!8 = !{i32 7, !"Dwarf Version", i32 4}
!9 = !{i32 2, !"Debug Info Version", i32 3}
!10 = !{i32 1, !"wchar_size", i32 4}
!11 = !{i32 7, !"PIC Level", i32 2}
!12 = !{!"clang"}
!13 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 5, type: !14, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !22)
!14 = !DISubroutineType(types: !15)
!15 = !{!7, !16}
!16 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "fat_ptr", file: !1, line: 1, size: 192, elements: !17)
!17 = !{!18, !20, !21}
!18 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !16, file: !1, line: 2, baseType: !19, size: 64)
!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
!20 = !DIDerivedType(tag: DW_TAG_member, name: "low", scope: !16, file: !1, line: 2, baseType: !19, size: 64, offset: 64)
!21 = !DIDerivedType(tag: DW_TAG_member, name: "high", scope: !16, file: !1, line: 2, baseType: !19, size: 64, offset: 128)
!22 = !{!23}
!23 = !DILocalVariable(name: "f", arg: 1, scope: !13, file: !1, line: 5, type: !16)
!24 = !DILocation(line: 5, column: 24, scope: !13)
!25 = !DILocation(line: 6, column: 23, scope: !13)
!31 = !DILocation(line: 6, column: 20, scope: !13)
!34 = !DILocation(line: 6, column: 16, scope: !13)
!35 = !DILocation(line: 6, column: 12, scope: !13)
!36 = !DILocation(line: 6, column: 5, scope: !13)

...
---
name: bar
stack:
- { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8,
stack-id: default, callee-saved-register: '$lr', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 1, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8,
stack-id: default, callee-saved-register: '$fp', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
callSites:
- { bb: 0, offset: 8, fwdArgRegs:
- { arg: 0, reg: '$w0' } }
- { bb: 0, offset: 10, fwdArgRegs:
- { arg: 0, reg: '$w0' } }
body: |
bb.0.entry:
liveins: $x0, $lr
DBG_VALUE $x0, 0, !23, !DIExpression(DW_OP_plus_uconst, 12), debug-location !24
early-clobber $sp = frame-setup STPXpre killed $fp, killed $lr, $sp, -2 :: (store 8 into %stack.1), (store 8 into %stack.0)
$fp = frame-setup ADDXri $sp, 0, 0
frame-setup CFI_INSTRUCTION def_cfa $w29, 16
frame-setup CFI_INSTRUCTION offset $w30, -8, debug-location !25
frame-setup CFI_INSTRUCTION offset $w29, -16, debug-location !25
renamable $x8 = LDRXui killed renamable $x0, 0, debug-location !25 :: (load 8 from %ir.ptr2)
renamable $w0 = LDRWui killed renamable $x8, 0, debug-location !31 :: (load 4 from %ir.0)
BL @baz, csr_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit $w0, implicit-def $sp, implicit-def $w0, debug-location !34
early-clobber $sp, $fp, $lr = frame-destroy LDPXpost $sp, 2, debug-location !35 :: (load 8 from %stack.1), (load 8 from %stack.0)
TCRETURNdi @baz, 0, csr_aarch64_aapcs, implicit $sp, implicit $w0, debug-location !35
...
117 changes: 117 additions & 0 deletions llvm/test/DebugInfo/MIR/AArch64/dbgcall-site-indirect-param.mir
@@ -0,0 +1,117 @@
# RUN: llc -start-before=livedebugvalues -stop-after=machineverifier -o - %s \
# RUN: | FileCheck %s -check-prefix=MIR

# RUN: llc -start-before=livedebugvalues -filetype=obj -o - %s \
# RUN: | llvm-dwarfdump - | FileCheck %s -check-prefix=DWARF -implicit-check-not=DW_OP_entry_value

# // Original Source
# struct fat_ptr {
# int *ptr, *low, *high;
# };
# extern int baz(int x);
# int bar(struct fat_ptr f) {
# return baz(baz(*f.ptr));
# }

# MIR: renamable $w0 = LDRWui killed renamable $x8
# MIR-NEXT: DBG_VALUE $x0, 0, {{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1)
# MIR-NEXT: BL @baz
# MIR-NEXT: frame-destroy LDPXpost
# MIR-NEXT: TCRETURNdi @baz

# After w0 is clobbered, we should get an indirect parameter entry value for "f".

# DWARF-LABEL: DW_TAG_formal_parameter
# DWARF-NEXT: DW_AT_location
# DWARF-NEXT: [0x0000000000000000, 0x0000000000000010): DW_OP_breg0 W0+0
# DWARF-NEXT: [0x0000000000000010, 0x000000000000001c): DW_OP_entry_value(DW_OP_reg0 W0))
# DWARF-NEXT: DW_AT_name ("f")

--- |
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
target triple = "arm64-apple-ios10.0.0"

%struct.fat_ptr = type { i32*, i32*, i32* }

define i32 @bar(%struct.fat_ptr* nocapture readonly %f) local_unnamed_addr !dbg !13 {
entry:
call void @llvm.dbg.declare(metadata %struct.fat_ptr* %f, metadata !23, metadata !DIExpression()), !dbg !24
%ptr2 = bitcast %struct.fat_ptr* %f to i32**, !dbg !25
%0 = load i32*, i32** %ptr2, align 8, !dbg !25
%1 = load i32, i32* %0, align 4, !dbg !31
%call = tail call i32 @baz(i32 %1), !dbg !34
%call1 = tail call i32 @baz(i32 %call), !dbg !35
ret i32 %call1, !dbg !36
}

declare void @llvm.dbg.declare(metadata, metadata, metadata)

declare !dbg !4 i32 @baz(i32) local_unnamed_addr optsize

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!8, !9, !10, !11}
!llvm.ident = !{!12}

!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None, sysroot: "/")
!1 = !DIFile(filename: "indirect.c", directory: "/tmp/fatptr")
!2 = !{}
!3 = !{!4}
!4 = !DISubprogram(name: "baz", scope: !1, file: !1, line: 4, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
!5 = !DISubroutineType(types: !6)
!6 = !{!7, !7}
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!8 = !{i32 7, !"Dwarf Version", i32 4}
!9 = !{i32 2, !"Debug Info Version", i32 3}
!10 = !{i32 1, !"wchar_size", i32 4}
!11 = !{i32 7, !"PIC Level", i32 2}
!12 = !{!"clang"}
!13 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 5, type: !14, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !22)
!14 = !DISubroutineType(types: !15)
!15 = !{!7, !16}
!16 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "fat_ptr", file: !1, line: 1, size: 192, elements: !17)
!17 = !{!18, !20, !21}
!18 = !DIDerivedType(tag: DW_TAG_member, name: "ptr", scope: !16, file: !1, line: 2, baseType: !19, size: 64)
!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
!20 = !DIDerivedType(tag: DW_TAG_member, name: "low", scope: !16, file: !1, line: 2, baseType: !19, size: 64, offset: 64)
!21 = !DIDerivedType(tag: DW_TAG_member, name: "high", scope: !16, file: !1, line: 2, baseType: !19, size: 64, offset: 128)
!22 = !{!23}
!23 = !DILocalVariable(name: "f", arg: 1, scope: !13, file: !1, line: 5, type: !16)
!24 = !DILocation(line: 5, column: 24, scope: !13)
!25 = !DILocation(line: 6, column: 23, scope: !13)
!31 = !DILocation(line: 6, column: 20, scope: !13)
!34 = !DILocation(line: 6, column: 16, scope: !13)
!35 = !DILocation(line: 6, column: 12, scope: !13)
!36 = !DILocation(line: 6, column: 5, scope: !13)

...
---
name: bar
stack:
- { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8,
stack-id: default, callee-saved-register: '$lr', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
- { id: 1, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8,
stack-id: default, callee-saved-register: '$fp', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
callSites:
- { bb: 0, offset: 8, fwdArgRegs:
- { arg: 0, reg: '$w0' } }
- { bb: 0, offset: 10, fwdArgRegs:
- { arg: 0, reg: '$w0' } }
body: |
bb.0.entry:
liveins: $x0, $lr
DBG_VALUE $x0, 0, !23, !DIExpression(), debug-location !24
early-clobber $sp = frame-setup STPXpre killed $fp, killed $lr, $sp, -2 :: (store 8 into %stack.1), (store 8 into %stack.0)
$fp = frame-setup ADDXri $sp, 0, 0
frame-setup CFI_INSTRUCTION def_cfa $w29, 16
frame-setup CFI_INSTRUCTION offset $w30, -8, debug-location !25
frame-setup CFI_INSTRUCTION offset $w29, -16, debug-location !25
renamable $x8 = LDRXui killed renamable $x0, 0, debug-location !25 :: (load 8 from %ir.ptr2)
renamable $w0 = LDRWui killed renamable $x8, 0, debug-location !31 :: (load 4 from %ir.0)
BL @baz, csr_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit $w0, implicit-def $sp, implicit-def $w0, debug-location !34
early-clobber $sp, $fp, $lr = frame-destroy LDPXpost $sp, 2, debug-location !35 :: (load 8 from %stack.1), (load 8 from %stack.0)
TCRETURNdi @baz, 0, csr_aarch64_aapcs, implicit $sp, implicit $w0, debug-location !35
...

0 comments on commit 15919fb

Please sign in to comment.