Skip to content

Cherry-pick LiveDebugValues bugfix #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 30, 2019
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
4 changes: 4 additions & 0 deletions llvm/include/llvm/IR/DebugInfoMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -2464,6 +2464,10 @@ class DIExpression : public MDNode {
/// Return whether this is an implicit location description.
bool isImplicit() const;

/// Return whether the location is computed on the expression stack, meaning
/// it cannot be a simple register location.
bool isComplex() const;

/// Append \p Ops with operations to apply the \p Offset.
static void appendOffset(SmallVectorImpl<uint64_t> &Ops, int64_t Offset);

Expand Down
17 changes: 15 additions & 2 deletions llvm/lib/CodeGen/LiveDebugValues.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -642,9 +642,17 @@ void LiveDebugValues::insertTransferDebugPair(
"No register supplied when handling a restore of a debug value");
MachineFunction *MF = MI.getMF();
DIBuilder DIB(*const_cast<Function &>(MF->getFunction()).getParent());

const DIExpression *NewExpr;
if (auto Fragment = DebugInstr->getDebugExpression()->getFragmentInfo())
NewExpr = *DIExpression::createFragmentExpression(DIB.createExpression(),
Fragment->OffsetInBits, Fragment->SizeInBits);
else
NewExpr = DIB.createExpression();

NewDebugInstr =
BuildMI(*MF, DebugInstr->getDebugLoc(), DebugInstr->getDesc(), false,
NewReg, DebugInstr->getDebugVariable(), DIB.createExpression());
NewReg, DebugInstr->getDebugVariable(), NewExpr);
VarLoc VL(*NewDebugInstr, LS);
ProcessVarLoc(VL, NewDebugInstr);
LLVM_DEBUG(dbgs() << "Creating DBG_VALUE inst for register restore: ";
Expand Down Expand Up @@ -792,9 +800,14 @@ void LiveDebugValues::transferSpillOrRestoreInst(MachineInstr &MI,
<< "\n");
}
// Check if the register or spill location is the location of a debug value.
// FIXME: Don't create a spill transfer if there is a complex expression,
// because we currently cannot recover the original expression on restore.
for (unsigned ID : OpenRanges.getVarLocs()) {
const MachineInstr *DebugInstr = &VarLocIDs[ID].MI;

if (TKind == TransferKind::TransferSpill &&
VarLocIDs[ID].isDescribedByReg() == Reg) {
VarLocIDs[ID].isDescribedByReg() == Reg &&
!DebugInstr->getDebugExpression()->isComplex()) {
LLVM_DEBUG(dbgs() << "Spilling Register " << printReg(Reg, TRI) << '('
<< VarLocIDs[ID].Var.getVar()->getName() << ")\n");
} else if (TKind == TransferKind::TransferRestore &&
Expand Down
13 changes: 11 additions & 2 deletions llvm/lib/CodeGen/PrologEpilogInserter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,16 @@ void PEI::replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &MF,
MI.getOperand(0).setIsDebug();

const DIExpression *DIExpr = MI.getDebugExpression();

// If we have a direct DBG_VALUE, and its location expression isn't
// currently complex, then adding an offset will morph it into a
// complex location that is interpreted as being a memory address.
// This changes a pointer-valued variable to dereference that pointer,
// which is incorrect. Fix by adding DW_OP_stack_value.
unsigned PrependFlags = DIExpression::ApplyOffset;
if (!MI.isIndirectDebugValue() && !DIExpr->isComplex())
PrependFlags |= DIExpression::StackValue;

// If we have DBG_VALUE that is indirect and has a Implicit location
// expression need to insert a deref before prepending a Memory
// location expression. Also after doing this we change the DBG_VALUE
Expand All @@ -1198,8 +1208,7 @@ void PEI::replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &MF,
// Make the DBG_VALUE direct.
MI.getOperand(1).ChangeToRegister(0, false);
}
DIExpr =
DIExpression::prepend(DIExpr, DIExpression::ApplyOffset, Offset);
DIExpr = DIExpression::prepend(DIExpr, PrependFlags, Offset);
MI.getOperand(3).setMetadata(DIExpr);
continue;
}
Expand Down
21 changes: 21 additions & 0 deletions llvm/lib/IR/DebugInfoMetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,27 @@ bool DIExpression::isImplicit() const {
return false;
}

bool DIExpression::isComplex() const {
if (!isValid())
return false;

if (getNumElements() == 0)
return false;

// If there are any elements other than fragment or tag_offset, then some
// kind of complex computation occurs.
for (const auto &It : expr_ops()) {
switch (It.getOp()) {
case dwarf::DW_OP_LLVM_tag_offset:
case dwarf::DW_OP_LLVM_fragment:
continue;
default: return true;
}
}

return false;
}

Optional<DIExpression::FragmentInfo>
DIExpression::getFragmentInfo(expr_op_iterator Start, expr_op_iterator End) {
for (auto I = Start; I != End; ++I)
Expand Down
115 changes: 113 additions & 2 deletions llvm/test/DebugInfo/MIR/X86/live-debug-values-restore.mir
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@
# return *(p + 1);
# }

# Pick out DILocalVariable numbers for "p" and "q"
# CHECK: ![[PVAR:[0-9]+]] = !DILocalVariable(name: "p",
# CHECK: ![[QVAR:[0-9]+]] = !DILocalVariable(name: "q",

# Ascertain that the spill has been recognized and manifested in a DBG_VALUE.
# CHECK: MOV64mr $rsp,{{.*-8.*}}killed{{.*}}$rdi :: (store 8 into %stack.0)
# CHECK-NEXT: DBG_VALUE $rsp,{{.*}}![[MDIX:[0-9]+]],{{.*}}!DIExpression(DW_OP_constu, 8, DW_OP_minus)
# CHECK-NEXT: DBG_VALUE $rsp,{{.*}}![[PVAR]],{{.*}}!DIExpression(DW_OP_constu, 8, DW_OP_minus)

# Check for the restore.
# CHECK: $rdi = MOV64rm $rsp,{{.*-8.*}}:: (load 8 from %stack.0)
# CHECK-NEXT: DBG_VALUE $rdi,{{.*}}![[MDIX]], !DIExpression()
# CHECK-NEXT: DBG_VALUE $rdi,{{.*}}![[PVAR]], !DIExpression()

--- |
define dso_local i32 @f(i32* readonly %p) local_unnamed_addr !dbg !7 {
Expand All @@ -39,6 +43,22 @@
ret i32 %0, !dbg !28
}

define dso_local i32 @g(i32* readonly %p) local_unnamed_addr !dbg !107 {
entry:
call void @llvm.dbg.value(metadata i32* %p, metadata !113, metadata !DIExpression()), !dbg !114
%tobool = icmp eq i32* %p, null, !dbg !115
br i1 %tobool, label %if.end, label %if.then, !dbg !117

if.then: ; preds = %entry
tail call void asm sideeffect "", "~{rax},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{rbp},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{dirflag},~{fpsr},~{flags}"(), !dbg !118, !srcloc !120
br label %if.end, !dbg !121

if.end: ; preds = %entry, %if.then
%add.ptr = getelementptr inbounds i32, i32* %p, i64 1, !dbg !122
%0 = load i32, i32* %add.ptr, align 4, !dbg !123, !tbaa !24
ret i32 %0, !dbg !128
}

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

!llvm.dbg.cu = !{!0}
Expand Down Expand Up @@ -74,6 +94,22 @@
!26 = !{!"omnipotent char", !27, i64 0}
!27 = !{!"Simple C/C++ TBAA"}
!28 = !DILocation(line: 9, column: 3, scope: !7)
!101 = !DIBasicType(name: "looong int", size: 64, encoding: DW_ATE_signed)
!107 = distinct !DISubprogram(name: "g", scope: !1, file: !1, line: 105, type: !8, scopeLine: 105, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !112)
!112 = !{!113}
!113 = !DILocalVariable(name: "q", arg: 1, scope: !107, file: !1, line: 105, type: !101)
!114 = !DILocation(line: 105, column: 12, scope: !107)
!115 = !DILocation(line: 106, column: 7, scope: !116)
!116 = distinct !DILexicalBlock(scope: !107, file: !1, line: 106, column: 7)
!117 = !DILocation(line: 106, column: 7, scope: !107)
!118 = !DILocation(line: 107, column: 5, scope: !119)
!119 = distinct !DILexicalBlock(scope: !116, file: !1, line: 106, column: 10)
!120 = !{i32 -2147471544}
!121 = !DILocation(line: 108, column: 3, scope: !119)
!122 = !DILocation(line: 109, column: 14, scope: !107)
!123 = !DILocation(line: 109, column: 10, scope: !107)
!128 = !DILocation(line: 109, column: 3, scope: !107)


...
---
Expand Down Expand Up @@ -187,3 +223,78 @@ body: |
RETQ $eax, debug-location !28

...
---
# This second function has been appended as a regression test against a
# crash, caused by expressions being created from spill restores that did
# not preserve fragment information. Test that no empty expressions are
# created at all, and the last block describes both variable fragments.

# CHECK-LABEL: name: g
# CHECK-NOT: !DIExpression()
# CHECK-LABEL: bb.2.if.end:
# CHECK: DBG_VALUE $rdi, $noreg, ![[QVAR]], !DIExpression(DW_OP_LLVM_fragment, 0, 32)
# CHECK-NEXT: DBG_VALUE $rbx, $noreg, ![[QVAR]], !DIExpression(DW_OP_LLVM_fragment, 32, 32)

name: g
alignment: 4
tracksRegLiveness: true
liveins:
- { reg: '$rdi', virtual-reg: '' }
frameInfo:
stackSize: 48
offsetAdjustment: -48
maxAlignment: 8
cvBytesOfCalleeSavedRegisters: 48
localFrameSize: 0
fixedStack:
- { id: 0, type: spill-slot, offset: -56, size: 8, alignment: 8, stack-id: default,
callee-saved-register: '$rbx', callee-saved-restored: true, debug-info-variable: '',
debug-info-expression: '', debug-info-location: '' }
- { id: 1, type: spill-slot, offset: -48, size: 8, alignment: 16, stack-id: default,
callee-saved-register: '$r12', callee-saved-restored: true, debug-info-variable: '',
debug-info-expression: '', debug-info-location: '' }
- { id: 2, type: spill-slot, offset: -40, size: 8, alignment: 8, stack-id: default,
callee-saved-register: '$r13', callee-saved-restored: true, debug-info-variable: '',
debug-info-expression: '', debug-info-location: '' }
- { id: 3, type: spill-slot, offset: -32, size: 8, alignment: 16, stack-id: default,
callee-saved-register: '$r14', callee-saved-restored: true, debug-info-variable: '',
debug-info-expression: '', debug-info-location: '' }
- { id: 4, type: spill-slot, offset: -24, size: 8, alignment: 8, stack-id: default,
callee-saved-register: '$r15', callee-saved-restored: true, debug-info-variable: '',
debug-info-expression: '', debug-info-location: '' }
- { id: 5, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: default,
callee-saved-register: '$rbp', callee-saved-restored: true, debug-info-variable: '',
debug-info-expression: '', debug-info-location: '' }
stack:
- { id: 0, name: '', type: spill-slot, offset: -64, size: 8, alignment: 8,
stack-id: default, callee-saved-register: '', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
constants: []
body: |
bb.0.entry:
successors: %bb.1(0x50000000)
liveins: $rdi, $rbx, $r12, $r13, $r14, $r15, $rbp

DBG_VALUE $rdi, $noreg, !113, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !114
TEST64rr renamable $rdi, renamable $rdi, implicit-def $eflags, debug-location !115
JMP_1 %bb.1, implicit $eflags, debug-location !117

bb.1.if.then:
successors: %bb.2(0x80000000)
liveins: $rdi, $rbp, $r15, $r14, $r13, $r12, $rbx

MOV64mr $rsp, 1, $noreg, -8, $noreg, killed renamable $rdi :: (store 8 into %stack.0)
renamable $rdi = MOV64rm $rsp, 1, $noreg, -8, $noreg :: (load 8 from %stack.0)

bb.2.if.end:
liveins: $rdi, $rbx, $r12, $r13, $r14, $r15, $rbp

DBG_VALUE $rbx, $noreg, !113, !DIExpression(DW_OP_LLVM_fragment, 32, 32), debug-location !114
MOV64mr $rsp, 1, $noreg, -8, $noreg, killed renamable $rbx :: (store 8 into %stack.0)
renamable $rsi = MOV64rm $rsp, 1, $noreg, -8, $noreg :: (load 8 from %stack.0)

renamable $eax = MOV32rm killed renamable $rsi, 1, $noreg, 4, $noreg, debug-location !123 :: (load 4 from %ir.add.ptr, !tbaa !24)
$rdi = MOV64ri 0
RETQ $eax, debug-location !128

...
130 changes: 130 additions & 0 deletions llvm/test/DebugInfo/MIR/X86/prolog-epilog-indirection.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# RUN: llc %s -x mir -o - -mtriple=x86_64-unknown-unknown -run-pass=prologepilog | FileCheck %s
#
# Check when the DBG_VALUE on a stack slot below (for var "c") has its stack
# slot replaced with $rsp and a complex expression, it has DW_OP_stack_value
# added. A direct reference to the stack slot is considered to be the _address_
# of that stack slot, wheras its contents would be an indirect DBG_VALUE.
#
# Check too that for the same DBG_VALUE inst, with an indirect reference to
# the stack slot, we do _not_ get DW_OP_plus_uconst added. This expression
# should remain indirect, referring to the contents of the stack slot.
#
# CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "c"
# CHECK: ![[VAR2:[0-9]+]] = !DILocalVariable(name: "asdf"
# CHECK: ![[VAR3:[0-9]+]] = !DILocalVariable(name: "bees"
#
# CHECK: LEA64r $rsp
# CHECK-NEXT: DBG_VALUE $rsp, $noreg, ![[VAR]], !DIExpression(DW_OP_plus_uconst, {{[0-9]+}}, DW_OP_stack_value)
# CHECK-NEXT: DBG_VALUE $rsp, $noreg, ![[VAR2]], !DIExpression(DW_OP_plus_uconst, {{[0-9]+}}, DW_OP_stack_value, DW_OP_LLVM_fragment, 1, 2)
# CHECK-NEXT: DBG_VALUE $rsp, $noreg, ![[VAR3]], !DIExpression(DW_OP_plus_uconst, {{[0-9]+}}, DW_OP_LLVM_tag_offset, 0, DW_OP_stack_value)
# CHECK-NEXT: DBG_VALUE 1834104526
# CHECK-NEXT: MOV64mr
# CHECK-NEXT: DBG_VALUE $rsp, 0, ![[VAR]], !DIExpression(DW_OP_plus_uconst, {{[0-9]+}})

--- |
; ModuleID = 'out.ll'
source_filename = "abc.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@b = common dso_local local_unnamed_addr global i32* null, align 8, !dbg !0
@a = common dso_local local_unnamed_addr global i32 0, align 4, !dbg !6

; Function Attrs: nounwind uwtable
define dso_local i32 @main() local_unnamed_addr !dbg !14 {
entry:
%l_1081 = alloca i32, align 4
%0 = bitcast i32* %l_1081 to i8*, !dbg !20
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0), !dbg !20
call void @llvm.dbg.value(metadata i32 1834104526, metadata !18, metadata !DIExpression()), !dbg !21
call void @llvm.dbg.value(metadata i32* %l_1081, metadata !19, metadata !DIExpression()), !dbg !21
store i32* %l_1081, i32** @b, align 8, !dbg !22, !tbaa !23
store i32 9, i32* @a, align 4, !dbg !27, !tbaa !28
store i32 9, i32* %l_1081, align 4, !dbg !30, !tbaa !28
%call = call i32 (...) @optimize_me_not(), !dbg !31
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0), !dbg !32
ret i32 0, !dbg !32
}

; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture)

declare dso_local i32 @optimize_me_not(...) local_unnamed_addr

; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture)

; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata)

!llvm.dbg.cu = !{!2}
!llvm.module.flags = !{!10, !11, !12}
!llvm.ident = !{!13}

!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 2, type: !9, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None)
!3 = !DIFile(filename: "abc.c", directory: ".")
!4 = !{}
!5 = !{!6, !0}
!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression())
!7 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 1, type: !8, isLocal: false, isDefinition: true)
!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64)
!10 = !{i32 2, !"Dwarf Version", i32 4}
!11 = !{i32 2, !"Debug Info Version", i32 3}
!12 = !{i32 1, !"wchar_size", i32 4}
!13 = !{!"clang"}
!14 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 3, type: !15, scopeLine: 3, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !17)
!15 = !DISubroutineType(types: !16)
!16 = !{!8}
!17 = !{!18, !19, !33, !34}
!18 = !DILocalVariable(name: "l_1081", scope: !14, file: !3, line: 4, type: !8)
!19 = !DILocalVariable(name: "c", scope: !14, file: !3, line: 5, type: !9)
!20 = !DILocation(line: 4, column: 3, scope: !14)
!21 = !DILocation(line: 0, scope: !14)
!22 = !DILocation(line: 6, column: 5, scope: !14)
!23 = !{!24, !24, i64 0}
!24 = !{!"any pointer", !25, i64 0}
!25 = !{!"omnipotent char", !26, i64 0}
!26 = !{!"Simple C/C++ TBAA"}
!27 = !DILocation(line: 7, column: 10, scope: !14)
!28 = !{!29, !29, i64 0}
!29 = !{!"int", !25, i64 0}
!30 = !DILocation(line: 7, column: 6, scope: !14)
!31 = !DILocation(line: 8, column: 3, scope: !14)
!32 = !DILocation(line: 9, column: 1, scope: !14)
!33 = !DILocalVariable(name: "asdf", scope: !14, file: !3, line: 4, type: !8)
!34 = !DILocalVariable(name: "bees", scope: !14, file: !3, line: 4, type: !8)

...
---
name: main
alignment: 4
tracksRegLiveness: true
frameInfo:
maxAlignment: 4
hasCalls: true
stack:
- { id: 0, name: l_1081, type: default, offset: 0, size: 4, alignment: 4,
callee-saved-register: '', callee-saved-restored: true,
debug-info-variable: '', debug-info-expression: '', debug-info-location: '' }
body: |
bb.0.entry:
renamable $rax = LEA64r %stack.0.l_1081, 1, $noreg, 0, $noreg
DBG_VALUE %stack.0.l_1081, $noreg, !19, !DIExpression(), debug-location !21
DBG_VALUE %stack.0.l_1081, $noreg, !33, !DIExpression(DW_OP_LLVM_fragment, 1, 2), debug-location !21
DBG_VALUE %stack.0.l_1081, $noreg, !34, !DIExpression(DW_OP_LLVM_tag_offset, 0), debug-location !21
DBG_VALUE 1834104526, $noreg, !18, !DIExpression(), debug-location !21
MOV64mr $rip, 1, $noreg, @b, $noreg, killed renamable $rax, debug-location !22 :: (store 8 into @b, !tbaa !23)
DBG_VALUE %stack.0.l_1081, 0, !19, !DIExpression(), debug-location !21
MOV32mi $rip, 1, $noreg, @a, $noreg, 9, debug-location !27 :: (store 4 into @a, !tbaa !28)
MOV32mi %stack.0.l_1081, 1, $noreg, 0, $noreg, 9, debug-location !30 :: (store 4 into %ir.l_1081, !tbaa !28)
ADJCALLSTACKDOWN64 0, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !31
dead $eax = MOV32r0 implicit-def dead $eflags, implicit-def $al, debug-location !31
CALL64pcrel32 @optimize_me_not, csr_64, implicit $rsp, implicit $ssp, implicit $al, implicit-def $rsp, implicit-def $ssp, implicit-def dead $eax, debug-location !31
ADJCALLSTACKUP64 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !31
$eax = MOV32r0 implicit-def dead $eflags, debug-location !32
RET 0, $eax, debug-location !32

...