From 5d0e6b6755da38084809d38bf527164a7f285ecd Mon Sep 17 00:00:00 2001 From: David Stenberg Date: Tue, 28 May 2019 13:23:25 +0000 Subject: [PATCH] Stop undef fragments from closing non-overlapping fragments Summary: When DwarfDebug::buildLocationList() encountered an undef debug value, it would truncate all open values, regardless if they were overlapping or not. This patch fixes so that it only does that for overlapping fragments. This change unearthed a bug that I had introduced in D57511, which I have fixed in this patch. The code in DebugHandlerBase that changes labels for parameter debug values could break DwarfDebug's assumption that the labels for the entries in the debug value history are monotonically increasing. Before this patch, that bug could result in location list entries whose ending address was lower than the beginning address, and with the changes for undef debug values that this patch introduces it could trigger an assertion, due to attempting to emit location list entries with empty ranges. A reproducer for the bug is added in param-reg-const-mix.mir. Reviewers: aprantl, jmorse, probinson Reviewed By: aprantl Subscribers: javed.absar, llvm-commits Tags: #debug-info, #llvm Differential Revision: https://reviews.llvm.org/D62379 llvm-svn: 361820 --- llvm/include/llvm/CodeGen/MachineInstr.h | 6 ++ llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp | 9 +- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 22 +++-- .../test/DebugInfo/MIR/ARM/param-reg-const-mix.mir | 96 ++++++++++++++++++++++ llvm/test/DebugInfo/X86/undef-fragment.ll | 78 ++++++++++++++++++ 5 files changed, 197 insertions(+), 14 deletions(-) create mode 100644 llvm/test/DebugInfo/MIR/ARM/param-reg-const-mix.mir create mode 100644 llvm/test/DebugInfo/X86/undef-fragment.ll diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h index 221510f..42889dc 100644 --- a/llvm/include/llvm/CodeGen/MachineInstr.h +++ b/llvm/include/llvm/CodeGen/MachineInstr.h @@ -1005,6 +1005,12 @@ public: && getOperand(1).isImm(); } + /// Return true if the instruction is a debug value which describes a part of + /// a variable as unavailable. + bool isUndefDebugValue() const { + return isDebugValue() && getOperand(0).isReg() && !getOperand(0).getReg(); + } + bool isPHI() const { return getOpcode() == TargetOpcode::PHI || getOpcode() == TargetOpcode::G_PHI; diff --git a/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp b/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp index 22c28cc..22f458e 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp @@ -246,8 +246,13 @@ void DebugHandlerBase::beginFunction(const MachineFunction *MF) { Pred.getInstr()->getDebugExpression()); })) break; - if (!IsDescribedByReg(I->getInstr())) - LabelsBeforeInsn[I->getInstr()] = Asm->getFunctionBegin(); + // The code that generates location lists for DWARF assumes that the + // entries' start labels are monotonically increasing, and since we + // don't change the label for fragments that are described by + // registers, we must bail out when encountering such a fragment. + if (IsDescribedByReg(I->getInstr())) + break; + LabelsBeforeInsn[I->getInstr()] = Asm->getFunctionBegin(); } } } diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 070b1b6..f5501a1 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1139,16 +1139,6 @@ void DwarfDebug::buildLocationList(SmallVectorImpl &DebugLoc, for (auto EB = Entries.begin(), EI = EB, EE = Entries.end(); EI != EE; ++EI) { const MachineInstr *Instr = EI->getInstr(); - if (EI->isDbgValue()) { - // Check if a variable is inaccessible in this range. - // TODO: This should only truncate open ranges that are overlapping. - if (Instr->getNumOperands() > 1 && - Instr->getOperand(0).isReg() && !Instr->getOperand(0).getReg()) { - OpenRanges.clear(); - continue; - } - } - // Remove all values that are no longer live. size_t Index = std::distance(EB, EI); auto Last = @@ -1177,8 +1167,16 @@ void DwarfDebug::buildLocationList(SmallVectorImpl &DebugLoc, // If this history map entry has a debug value, add that to the list of // open ranges. if (EI->isDbgValue()) { - auto Value = getDebugLocValue(Instr); - OpenRanges.emplace_back(EI->getEndIndex(), Value); + // Do not add undef debug values, as they are redundant information in + // the location list entries. An undef debug results in an empty location + // description. If there are any non-undef fragments then padding pieces + // with empty location descriptions will automatically be inserted, and if + // all fragments are undef then the whole location list entry is + // redundant. + if (!Instr->isUndefDebugValue()) { + auto Value = getDebugLocValue(Instr); + OpenRanges.emplace_back(EI->getEndIndex(), Value); + } } // Location list entries with empty location descriptions are redundant diff --git a/llvm/test/DebugInfo/MIR/ARM/param-reg-const-mix.mir b/llvm/test/DebugInfo/MIR/ARM/param-reg-const-mix.mir new file mode 100644 index 0000000..9951128 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/ARM/param-reg-const-mix.mir @@ -0,0 +1,96 @@ +# RUN: llc -mtriple=armv4t-unknown-unknown -start-after=livedebugvalues -filetype=obj -o - %s | llvm-dwarfdump - | FileCheck %s + +# This reproducer is based on the following C code: +# +# struct S0 { int f1; int f2; int f3; }; +# +# int a; +# +# void fn1(struct S0 p1) { +# a = p1.f1 >= fn2(p1.f2); +# } +# +# and was generated using the following commands: +# $ clang -O1 -g --target=armv4t -S -emit-llvm +# $ llc -O1 -stop-after=livedebugvalues + +--- | + target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" + target triple = "armv4t-unknown-unknown" + + ; Function Attrs: nounwind + define arm_aapcscc i32 @fn1([3 x i32] %p1.coerce) !dbg !7 { + entry: + %p1.coerce.fca.0.extract = extractvalue [3 x i32] %p1.coerce, 0 + call void @llvm.dbg.value(metadata i32 %p1.coerce.fca.0.extract, metadata !17, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !18 + %p1.coerce.fca.1.extract = extractvalue [3 x i32] %p1.coerce, 1 + call void @llvm.dbg.value(metadata i32 %p1.coerce.fca.1.extract, metadata !17, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32)), !dbg !18 + call void @llvm.dbg.value(metadata i32 undef, metadata !17, metadata !DIExpression(DW_OP_LLVM_fragment, 64, 32)), !dbg !18 + %call = tail call arm_aapcscc i32 bitcast (i32 (...)* @fn2 to i32 (i32)*)(i32 %p1.coerce.fca.1.extract), !dbg !19 + %cmp = icmp sge i32 %p1.coerce.fca.0.extract, %call, !dbg !19 + %conv = zext i1 %cmp to i32, !dbg !19 + ret i32 %conv, !dbg !19 + } + + declare arm_aapcscc i32 @fn2(...) + + ; Function Attrs: nounwind readnone speculatable + declare void @llvm.dbg.value(metadata, metadata, metadata) #0 + + attributes #0 = { nounwind readnone speculatable } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) + !1 = !DIFile(filename: "test.c", directory: "/") + !2 = !{} + !3 = !{i32 2, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"min_enum_size", i32 4} + !6 = !{!"clang version 9.0.0"} + !7 = distinct !DISubprogram(name: "fn1", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !16) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !11} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "S0", file: !1, line: 1, size: 96, elements: !12) + !12 = !{!13, !14, !15} + !13 = !DIDerivedType(tag: DW_TAG_member, name: "f1", scope: !11, file: !1, line: 1, baseType: !10, size: 32) + !14 = !DIDerivedType(tag: DW_TAG_member, name: "f2", scope: !11, file: !1, line: 1, baseType: !10, size: 32, offset: 32) + !15 = !DIDerivedType(tag: DW_TAG_member, name: "f3", scope: !11, file: !1, line: 1, baseType: !10, size: 32, offset: 64) + !16 = !{!17} + !17 = !DILocalVariable(name: "p1", arg: 1, scope: !7, file: !1, line: 3, type: !11) + !18 = !DILocation(line: 3, scope: !7) + !19 = !DILocation(line: 4, scope: !7) + +... +--- +name: fn1 +tracksRegLiveness: false +body: | + bb.0.entry: + $sp = frame-setup STMDB_UPD $sp, 14, $noreg, killed $r4, killed $lr + $r4 = MOVr $r0, 14, $noreg, $noreg + DBG_VALUE $r1, $noreg, !17, !DIExpression(DW_OP_LLVM_fragment, 32, 32), debug-location !18 + DBG_VALUE $noreg, $noreg, !17, !DIExpression(DW_OP_LLVM_fragment, 64, 32), debug-location !18 + DBG_VALUE $r4, $noreg, !17, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !18 + $r0 = MOVr killed $r1, 14, $noreg, $noreg, debug-location !19 + BL @fn2, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit $r0, implicit-def $sp, implicit-def $r0, debug-location !19 + renamable $r1 = MOVi 0, 14, $noreg, $noreg + CMPrr killed renamable $r4, killed renamable $r0, 14, $noreg, implicit-def $cpsr, debug-location !19 + $r1 = MOVi 1, 10, killed $cpsr, $noreg, implicit killed renamable $r1, debug-location !19 + $r0 = MOVr killed $r1, 14, $noreg, $noreg, debug-location !19 + $sp = LDMIA_UPD $sp, 14, $noreg, def $r4, def $lr, debug-location !19 + BX_RET 14, $noreg, implicit $r0, debug-location !19 + +... + +# Verify that the addresses in the location list for the parameter are +# monotonically increasing, and that the undef debug value fragment does not +# terminate the non-overlapping fragment that is described by $r1. + +# CHECK: DW_AT_location (0x00000000 +# CHECK-NEXT: [0x00000008, 0x00000010): DW_OP_reg4 R4, DW_OP_piece 0x4, DW_OP_reg1 R1, DW_OP_piece 0x4 +# CHECK-NEXT: [0x00000010, 0x00000024): DW_OP_reg4 R4, DW_OP_piece 0x4) +# CHECK-NEXT: DW_AT_name ("p1") diff --git a/llvm/test/DebugInfo/X86/undef-fragment.ll b/llvm/test/DebugInfo/X86/undef-fragment.ll new file mode 100644 index 0000000..e8c381a --- /dev/null +++ b/llvm/test/DebugInfo/X86/undef-fragment.ll @@ -0,0 +1,78 @@ +; RUN: llc -mtriple=x86_64-pc-linux-gnu -O2 -filetype=obj < %s | llvm-dwarfdump - | FileCheck %s + +; This reproducer is based on the following C code: +; +; typedef struct { int a; int b; } S; +; +; extern int ext(int); +; +; int foo() { +; S s = {123, 456}; +; ext(1); +; s.a = ext(2); +; ext(3); +; s.a = 789; +; return s.b; +; } +; +; and was generated using -O2 -g -fno-inline. +; +; As a small note, the third dbg.value's value has been changed from %call1 to +; undef (it would have become undef either way, but this was done to make the +; intention of the test a bit more clear). + +; Verify that a location list entry describing s.b is started at the +; non-overlapping undef debug value. + +; CHECK: DW_AT_location (0x00000000 +; CHECK-NEXT: {{0x[0-9a-f]+}}, [[ADDR1:0x[0-9a-f]+]]): DW_OP_constu 0x7b, DW_OP_stack_value, DW_OP_piece 0x4, DW_OP_constu 0x1c8, DW_OP_stack_value, DW_OP_piece 0x4 +; CHECK-NEXT: [[ADDR1]], [[ADDR2:0x[0-9a-f]+]]): DW_OP_piece 0x4, DW_OP_constu 0x1c8, DW_OP_stack_value, DW_OP_piece 0x4 +; CHECK-NEXT: [[ADDR2]], {{0x[0-9a-f]+}}): DW_OP_constu 0x315, DW_OP_stack_value, DW_OP_piece 0x4, DW_OP_constu 0x1c8, DW_OP_stack_value, DW_OP_piece 0x4) +; CHECK-NEXT: DW_AT_name ("s") + +; Function Attrs: noinline nounwind uwtable +define i32 @main() !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i32 123, metadata !12, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !18 + call void @llvm.dbg.value(metadata i32 456, metadata !12, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32)), !dbg !18 + %call = tail call i32 @ext(i32 1), !dbg !19 + %call1 = tail call i32 @ext(i32 2), !dbg !20 + call void @llvm.dbg.value(metadata i32 undef, metadata !12, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !18 + %call2 = tail call i32 @ext(i32 3), !dbg !21 + call void @llvm.dbg.value(metadata i32 789, metadata !12, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !18 + ret i32 456, !dbg !22 +} + +declare i32 @ext(i32) + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.value(metadata, metadata, metadata) #0 + +attributes #0 = { nounwind readnone speculatable } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "undef.c", directory: "/") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!6 = !{!"clang version 9.0.0"} +!7 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 5, type: !8, scopeLine: 5, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DILocalVariable(name: "s", scope: !7, file: !1, line: 6, type: !13) +!13 = !DIDerivedType(tag: DW_TAG_typedef, name: "S", file: !1, line: 1, baseType: !14) +!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !1, line: 1, size: 64, elements: !15) +!15 = !{!16, !17} +!16 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !14, file: !1, line: 1, baseType: !10, size: 32) +!17 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !14, file: !1, line: 1, baseType: !10, size: 32, offset: 32) +!18 = !DILocation(line: 6, scope: !7) +!19 = !DILocation(line: 7, scope: !7) +!20 = !DILocation(line: 8, scope: !7) +!21 = !DILocation(line: 9, scope: !7) +!22 = !DILocation(line: 11, scope: !7) -- 2.7.4