Index: llvm/include/llvm/CodeGen/MachineInstr.h =================================================================== --- llvm/include/llvm/CodeGen/MachineInstr.h +++ llvm/include/llvm/CodeGen/MachineInstr.h @@ -1005,6 +1005,12 @@ && 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; Index: llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp +++ llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp @@ -246,8 +246,13 @@ 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(); } } } Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1139,16 +1139,6 @@ 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 @@ // 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 Index: llvm/test/DebugInfo/MIR/ARM/param-reg-const-mix.mir =================================================================== --- /dev/null +++ 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") Index: llvm/test/DebugInfo/X86/undef-fragment.ll =================================================================== --- /dev/null +++ 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)