From 5c7cc6f83d1f3ea2016d94e1c9cc25f814d2671b Mon Sep 17 00:00:00 2001 From: David Stenberg Date: Fri, 13 Dec 2019 10:37:53 +0100 Subject: [PATCH] [LiveDebugValues] Omit entry values for DBG_VALUEs with pre-existing expressions Summary: This is a quickfix for PR44275. An assertion that checks that the DIExpression is valid failed due to attempting to create an entry value for an indirect parameter. This started appearing after D69028, as the indirect parameter started being represented using an DW_OP_deref, rather than with the DBG_VALUE's second operand, meaning that the isIndirectDebugValue() check in LiveDebugValues did not exclude such parameters. A DIExpression that has an entry value operation can currently not have any other operation, leading to the failed isValid() check. This patch simply makes us stop considering emitting entry values for such parameters. To support such cases I think we at least need to do the following changes: * In DIExpression::isValid(): Remove the limitation that a DW_OP_LLVM_entry_value operation can be the only operation in a DIExpression. * In LiveDebugValues::emitEntryValues(): Create an entry value of size 1, so that it only wraps the register operand, and not the whole pre-existing expression (the DW_OP_deref). * In LiveDebugValues::removeEntryValue(): Check that the new debug value has the same debug expression as the original, rather than checking that the debug expression is empty. * In DwarfExpression::addMachineRegExpression(): Modify the logic so that a DW_OP_reg* expression is emitted for the entry value. That is how GCC emits entry values for indirect parameters. That will currently not happen to due the DW_OP_deref causing the !HasComplexExpression to fail. The LocationKind needs to be changed also, rather than always emitting a DW_OP_stack_value for entry values. There are probably more things I have missed, but that could hopefully be a good starting point for emitting such entry values. Reviewers: djtodoro, aprantl, jmorse, vsk Reviewed By: aprantl, vsk Subscribers: hiraditya, llvm-commits Tags: #debug-info, #llvm Differential Revision: https://reviews.llvm.org/D71416 --- llvm/lib/CodeGen/LiveDebugValues.cpp | 5 +- .../MIR/X86/dbgcall-site-reference.mir | 118 ++++++++++++++++++ 2 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 llvm/test/DebugInfo/MIR/X86/dbgcall-site-reference.mir diff --git a/llvm/lib/CodeGen/LiveDebugValues.cpp b/llvm/lib/CodeGen/LiveDebugValues.cpp index 04efa7bc35e9..2226c10b49a4 100644 --- a/llvm/lib/CodeGen/LiveDebugValues.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues.cpp @@ -1428,8 +1428,9 @@ bool LiveDebugValues::isEntryValueCandidate( if (DefinedRegs.count(MI.getOperand(0).getReg())) return false; - // TODO: Add support for parameters that are described as fragments. - if (MI.getDebugExpression()->isFragment()) + // TODO: Add support for parameters that have a pre-existing debug expressions + // (e.g. fragments, or indirect parameters using DW_OP_deref). + if (MI.getDebugExpression()->getNumElements() > 0) return false; return true; diff --git a/llvm/test/DebugInfo/MIR/X86/dbgcall-site-reference.mir b/llvm/test/DebugInfo/MIR/X86/dbgcall-site-reference.mir new file mode 100644 index 000000000000..235787573f51 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dbgcall-site-reference.mir @@ -0,0 +1,118 @@ +# RUN: llc -debug-entry-values -start-before=livedebugvalues -filetype=obj -o - %s | llvm-dwarfdump - | FileCheck %s + +# Based on the following C++ code: +# struct A { A(A &) {} }; +# struct B { A a; }; +# struct C { C(B); }; +# struct D : C { D(B); }; +# D::D(B b) : C(b) {} + +# Reproducer for PR44275. Ideally we should get an entry value location list +# entry for the reference parameter b, but we are currently not able to do that +# due to limitations in the DWARF emission code, which puts restrictions on the +# DIExpression. For now verify that we don't crash when trying to add such an +# entry value. + +# CHECK: DW_AT_location +# CHECK-NEXT: [{{0x[0-9a-f]+}}, {{0x[0-9a-f]+}}): DW_OP_reg5 RDI +# CHECK-NEXT: [{{0x[0-9a-f]+}}, {{0x[0-9a-f]+}}): DW_OP_GNU_entry_value(DW_OP_reg5 RDI), DW_OP_stack_value) +# CHECK-NEXT: DW_AT_name ("this") + +# CHECK: DW_AT_location +# CHECK-NEXT: [0x0000000000000000, 0x0000000000000004): DW_OP_breg4 RSI+0) +# TODO: Here we should ideally get a location list entry using an entry value. +# CHECK-NEXT: DW_AT_name ("b") + +--- | + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + %struct.D = type { i8 } + %struct.B = type { %struct.A } + %struct.A = type { i8 } + %struct.C = type { i8 } + + @_ZN1DC1E1B = dso_local unnamed_addr alias void (%struct.D*, %struct.B*), void (%struct.D*, %struct.B*)* @_ZN1DC2E1B + ; Function Attrs: uwtable + define dso_local void @_ZN1DC2E1B(%struct.D* %this, %struct.B* nocapture readnone %b) unnamed_addr #0 align 2 !dbg !7 { + entry: + %agg.tmp = alloca %struct.B, align 1 + call void @llvm.dbg.value(metadata %struct.D* %this, metadata !32, metadata !DIExpression()), !dbg !35 + call void @llvm.dbg.declare(metadata %struct.B* %b, metadata !34, metadata !DIExpression()), !dbg !36 + %0 = bitcast %struct.D* %this to %struct.C*, !dbg !36 + call void @_ZN1CC2E1B(%struct.C* %0, %struct.B* nonnull %agg.tmp), !dbg !36 + ret void, !dbg !36 + } + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + + declare dso_local void @_ZN1CC2E1B(%struct.C*, %struct.B*) unnamed_addr + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + + attributes #0 = { uwtable } + attributes #1 = { nounwind readnone speculatable willreturn } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "foo.cpp", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 10.0.0"} + !7 = distinct !DISubprogram(name: "D", linkageName: "_ZN1DC2E1B", scope: !8, file: !1, line: 5, type: !28, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !27, retainedNodes: !31) + !8 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "D", file: !1, line: 4, size: 8, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !9, identifier: "_ZTS1D") + !9 = !{!10, !27} + !10 = !DIDerivedType(tag: DW_TAG_inheritance, scope: !8, baseType: !11, extraData: i32 0) + !11 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C", file: !1, line: 3, size: 8, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !12, identifier: "_ZTS1C") + !12 = !{!13} + !13 = !DISubprogram(name: "C", scope: !11, file: !1, line: 3, type: !14, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) + !14 = !DISubroutineType(types: !15) + !15 = !{null, !16, !17} + !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) + !17 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "B", file: !1, line: 2, size: 8, flags: DIFlagTypePassByReference | DIFlagNonTrivial, elements: !18, identifier: "_ZTS1B") + !18 = !{!19} + !19 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !17, file: !1, line: 2, baseType: !20, size: 8) + !20 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", file: !1, line: 1, size: 8, flags: DIFlagTypePassByReference | DIFlagNonTrivial, elements: !21, identifier: "_ZTS1A") + !21 = !{!22} + !22 = !DISubprogram(name: "A", scope: !20, file: !1, line: 1, type: !23, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) + !23 = !DISubroutineType(types: !24) + !24 = !{null, !25, !26} + !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !20, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) + !26 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !20, size: 64) + !27 = !DISubprogram(name: "D", scope: !8, file: !1, line: 4, type: !28, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) + !28 = !DISubroutineType(types: !29) + !29 = !{null, !30, !17} + !30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) + !31 = !{!32, !34} + !32 = !DILocalVariable(name: "this", arg: 1, scope: !7, type: !33, flags: DIFlagArtificial | DIFlagObjectPointer) + !33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64) + !34 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 5, type: !17) + !35 = !DILocation(line: 0, scope: !7) + !36 = !DILocation(line: 5, scope: !7) + +... +--- +name: _ZN1DC2E1B +body: | + bb.0.entry: + liveins: $rdi + + DBG_VALUE $rdi, $noreg, !32, !DIExpression(), debug-location !35 + DBG_VALUE $rsi, $noreg, !34, !DIExpression(DW_OP_deref), debug-location !36 + frame-setup PUSH64r undef $rax, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + $rsi = MOV64rr $rsp + DBG_VALUE $rdi, $noreg, !32, !DIExpression(), debug-location !35 + CALL64pcrel32 @_ZN1CC2E1B, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit-def $rsp, implicit-def $ssp, debug-location !36 + $rax = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !36 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !36 + RETQ debug-location !36 + +...