diff --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h --- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h +++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h @@ -633,6 +633,19 @@ /// Return true if Idx is a spill machine location. bool isSpill(LocIdx Idx) const { return LocIdxToLocID[Idx] >= NumRegs; } + /// How large is this location (aka, how wide is a value defined there?). + unsigned getLocSizeInBits(LocIdx L) const { + unsigned ID = LocIdxToLocID[L]; + if (!isSpill(L)) { + return TRI.getRegSizeInBits(Register(ID), MF.getRegInfo()); + } else { + // The slot location on the stack is uninteresting, we care about the + // position of the value within the slot (which comes with a size). + StackSlotPos Pos = locIDToSpillIdx(ID); + return Pos.first; + } + } + MLocIterator begin() { return MLocIterator(LocIdxToIDNum, 0); } MLocIterator end() { diff --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp --- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp @@ -870,19 +870,72 @@ // the variable is. if (Offset == 0) { const SpillLoc &Spill = SpillLocs[SpillID.id()]; - Expr = TRI.prependOffsetExpression(Expr, DIExpression::ApplyOffset, - Spill.SpillOffset); unsigned Base = Spill.SpillBase; MIB.addReg(Base); - MIB.addImm(0); - // Being on the stack makes this location indirect; if it was _already_ - // indirect though, we need to add extra indirection. See this test for - // a scenario where this happens: - // llvm/test/DebugInfo/X86/spill-nontrivial-param.ll + // There are several ways we can dereference things, and several inputs + // to consider: + // * NRVO variables will appear with IsIndirect set, but should have + // nothing else in their DIExpressions, + // * Variables with DW_OP_stack_value in their expr already need an + // explicit dereference of the stack location, + // * Values that don't match the variable size need DW_OP_deref_size, + // * Everything else can just become a simple location expression. + + // We need to use deref_size whenever there's a mismatch between the + // size of value and the size of variable portion being read. + // Additionally, we should use it whenever dealing with stack_value + // fragments, to avoid the consumer having to determine the deref size + // from DW_OP_piece. + bool UseDerefSize = false; + unsigned ValueSizeInBits = getLocSizeInBits(*MLoc); + unsigned DerefSizeInBytes = ValueSizeInBits / 8; + if (auto Fragment = Var.getFragment()) { + unsigned VariableSizeInBits = Fragment->SizeInBits; + if (VariableSizeInBits != ValueSizeInBits || Expr->isComplex()) + UseDerefSize = true; + } else if (auto Size = Var.getVariable()->getSizeInBits()) { + if (*Size != ValueSizeInBits) { + UseDerefSize = true; + } + } + if (Properties.Indirect) { - std::vector Elts = {dwarf::DW_OP_deref}; - Expr = DIExpression::append(Expr, Elts); + // This is something like an NRVO variable, where the pointer has been + // spilt to the stack, or a dbg.addr pointing at a coroutine frame + // field. It should end up being a memory location, with the pointer + // to the variable loaded off the stack with a deref. It can't be a + // DW_OP_stack_value expression. + assert(!Expr->isImplicit()); + Expr = TRI.prependOffsetExpression( + Expr, DIExpression::ApplyOffset | DIExpression::DerefAfter, + Spill.SpillOffset); + MIB.addImm(0); + } else if (UseDerefSize) { + // We're loading a value off the stack that's not the same size as the + // variable. Add / subtract stack offset, explicitly deref with a size, + // and add DW_OP_stack_value if not already present. + SmallVector Ops = {dwarf::DW_OP_deref_size, + DerefSizeInBytes}; + Expr = DIExpression::prependOpcodes(Expr, Ops, true); + unsigned Flags = DIExpression::StackValue | DIExpression::ApplyOffset; + Expr = TRI.prependOffsetExpression(Expr, Flags, Spill.SpillOffset); + MIB.addReg(0); + } else if (Expr->isComplex()) { + // A variable with no size ambiguity, but with extra elements in it's + // expression. Manually dereference the stack location. + assert(Expr->isComplex()); + Expr = TRI.prependOffsetExpression( + Expr, DIExpression::ApplyOffset | DIExpression::DerefAfter, + Spill.SpillOffset); + MIB.addReg(0); + } else { + // A plain value that has been spilt to the stack, with no further + // context. Request a location expression, marking the DBG_VALUE as + // IsIndirect. + Expr = TRI.prependOffsetExpression(Expr, DIExpression::ApplyOffset, + Spill.SpillOffset); + MIB.addImm(0); } } else { // This is a stack location with a weird subregister offset: emit an undef diff --git a/llvm/test/DebugInfo/MIR/InstrRef/deref-spills-with-size.mir b/llvm/test/DebugInfo/MIR/InstrRef/deref-spills-with-size.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/InstrRef/deref-spills-with-size.mir @@ -0,0 +1,371 @@ +# RUN: llc %s -o - -experimental-debug-variable-locations=true \ +# RUN: -run-pass=livedebugvalues \ +# RUN: | FileCheck %s --implicit-check-not=DBG_VALUE +# RUN: llc %s -o - -experimental-debug-variable-locations=true \ +# RUN: -start-before=livedebugvalues -filetype=obj \ +# RUN: | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF +# +# LLVM can produce DIExpressions that convert from one value of arbitrary size +# to another. This is normally fine, however that means the value for a +# variable tracked in instruction referencing might not be the same size as the +# variable itself. +# +# This becomes a problem when values move onto the stack and we emit +# DW_OP_deref: there is no information about how large a value the consumer +# should load from the stack. The convention today appears to be the size of +# the variable, and as a result if you have a value that's smaller than the +# variable size, extra information is loaded onto the DWARF expression stack. +# This then appears as a false variable value to the consumer. +# +# In this test, there are various scenarios to test where the size of the value +# needs to reach the expression emitter in LiveDebugValues. However, there are +# even more scenarios to consider where the value is in a spill slot, and: +# * The value is larger than the variable, the same, or smaller, +# * The variable is a normal scalar, or a fragment, +# * There is a DW_OP_stack_value in the expression, or not +# That gives us twelve situations to consider, and the easiest way to confirm +# correctness is to write them all out. The final DWARF expressions to +# produce are: +# * No stack value, +# * Scalar (32 bits), +# * Larger value (64 bits) +# DW_OP_breg7 RSP-8, DW_OP_deref_size 0x8, DW_OP_stack_value +# * Same value (32 bits) +# DW_OP_breg7 RSP-8 +# * Smaller value (8 bits) +# DW_OP_breg7 RSP-8, DW_OP_deref_size 0x1, DW_OP_stack_value +# * Fragment (32 bits) +# * Larger value (64 bits) +# DW_OP_breg7 RSP-8, DW_OP_deref_size 0x8, DW_OP_stack_value, DW_OP_piece 0x4 +# * Same value (32 bits) +# DW_OP_breg7 RSP-8, DW_OP_piece 0x4 +# * Smaller value (8 bits) +# DW_OP_breg7 RSP-8, DW_OP_deref_size 0x1, DW_OP_stack_value, DW_OP_piece 0x4 +# * Stack value (imagine there was some arithmetic on the stack before the +# DW_OP_stack_value opcode), +# * Scalar (32 bits), +# * Larger value (64 bits) +# DW_OP_breg7 RSP-8, DW_OP_deref_size 0x8, DW_OP_stack_value +# * Same value (32 bits) +# DW_OP_breg7 RSP-8, DW_OP_deref, DW_OP_stack_value +# * Smaller value (8 bits) +# DW_OP_breg7 RSP-8, DW_OP_deref_size 0x1, DW_OP_stack_value +# * Fragment (32 bits) +# * Larger value (64 bits) +# DW_OP_breg7 RSP-8, DW_OP_deref_size 0x8, DW_OP_stack_value, DW_OP_piece 0x4 +# * Same value (32 bits) +# DW_OP_breg7 RSP-8, DW_OP_deref_size 0x4, DW_OP_stack_value, DW_OP_piece 0x4 +# * Smaller value (8 bits) +# DW_OP_breg7 RSP-8, DW_OP_deref_size 0x1, DW_OP_stack_value, DW_OP_piece 0x4 +# +# The outlier from the pattern is (Stack value, Fragment, Same value size): we +# put a DW_OP_deref_size in where a DW_OP_deref could have sufficied. However, +# if we used DW_OP_deref, it would force the consumer to derive the deref size +# from the DW_OP_piece, which is un-necessarily hard. + +## Capture variable num, +# CHECK-DAG: ![[VARNUM:[0-9]+]] = !DILocalVariable(name: "flannel", +# CHECK-DAG: ![[VARNUM2:[0-9]+]] = !DILocalVariable(name: "shoes", + +# DWARF: DW_TAG_variable +# DWARF-NEXT: DW_AT_location +## Defined in register, spilt to stack. +# DWARF-NEXT: DW_OP_breg0 RAX+0, DW_OP_constu 0xff, DW_OP_and, DW_OP_convert (0x00000026) "DW_ATE_signed_8", DW_OP_convert (0x0000002a) "DW_ATE_signed_32", DW_OP_stack_value +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x1, DW_OP_convert (0x00000026) "DW_ATE_signed_8", DW_OP_convert (0x0000002a) "DW_ATE_signed_32", DW_OP_stack_value +## Defined on stack. +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x1, DW_OP_convert (0x00000026) "DW_ATE_signed_8", DW_OP_convert (0x0000002a) "DW_ATE_signed_32", DW_OP_stack_value +## DBG_PHI that moves to stack. +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x1, DW_OP_stack_value +## DBG_PHI of stack with size. +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x2, DW_OP_stack_value +## Followed by scalar / no-stack-value locations with various sizes. +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x8, DW_OP_stack_value +# DWARF-NEXT: DW_OP_breg7 RSP-8 +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x1, DW_OP_stack_value +## scalar / stack value with various sizes. +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x8, DW_OP_lit1, DW_OP_plus, DW_OP_stack_value +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref, DW_OP_lit1, DW_OP_plus, DW_OP_stack_value +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x1, DW_OP_lit1, DW_OP_plus, DW_OP_stack_value) +# DWARF: DW_AT_name ("flannel") + +# Variable with fragments. +# DWARF: DW_TAG_variable +# DWARF-NEXT: DW_AT_location +## Scalar / no-stack-value locations with various sizes. +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x8, DW_OP_stack_value, DW_OP_piece 0x4 +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_piece 0x4 +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x1, DW_OP_stack_value, DW_OP_piece 0x4 +## Scalar / stack value with various sizes. +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x8, DW_OP_lit1, DW_OP_plus, DW_OP_stack_value, DW_OP_piece 0x4 +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x4, DW_OP_lit1, DW_OP_plus, DW_OP_stack_value, DW_OP_piece 0x4 +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref_size 0x1, DW_OP_lit1, DW_OP_plus, DW_OP_stack_value, DW_OP_piece 0x4) +# DWARF: DW_AT_name ("shoes") + +--- | + ; ModuleID = 'missingvar.ll' + source_filename = "a" + 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" + + define linkonce_odr void @_ZNSt5dequeIPN4llvm4LoopESaIS2_EE13_M_insert_auxESt15_Deque_iteratorIS2_RS2_PS2_EmRKS2_() local_unnamed_addr align 2 !dbg !3 { + entry: + call void @llvm.dbg.value(metadata i32 0, metadata !8, metadata !DIExpression()), !dbg !7 + call void @llvm.dbg.value(metadata i32 0, metadata !10, metadata !DIExpression()), !dbg !7 + ret void + } + + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.module.flags = !{!0, !9} + !llvm.dbg.cu = !{!1} + + !0 = !{i32 2, !"Debug Info Version", i32 3} + !1 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !2, producer: "beards", isOptimized: true, runtimeVersion: 4, emissionKind: FullDebug) + !2 = !DIFile(filename: "bees.cpp", directory: "") + !3 = distinct !DISubprogram(name: "nope", scope: !2, file: !2, line: 1, type: !4, spFlags: DISPFlagDefinition, unit: !1) + !4 = !DISubroutineType(types: !5) + !5 = !{!6} + !6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !7 = !DILocation(line: 1, scope: !3) + !8 = !DILocalVariable(name: "flannel", scope: !3, type: !6) + !9 = !{i32 2, !"Dwarf Version", i32 5} + !10 = !DILocalVariable(name: "shoes", scope: !3, type: !11) + !11 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) + + +... +--- +name: _ZNSt5dequeIPN4llvm4LoopESaIS2_EE13_M_insert_auxESt15_Deque_iteratorIS2_RS2_PS2_EmRKS2_ +alignment: 16 +tracksRegLiveness: true +liveins: + - { reg: '$rdi' } + - { reg: '$rsi' } + - { reg: '$rdx' } +frameInfo: + stackSize: 48 + offsetAdjustment: -48 + maxAlignment: 8 + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 48 +fixedStack: + - { id: 0, type: spill-slot, offset: -56, size: 8, alignment: 8, callee-saved-register: '$rbx' } + - { id: 1, type: spill-slot, offset: -48, size: 8, alignment: 16, callee-saved-register: '$r12' } + - { id: 2, type: spill-slot, offset: -40, size: 8, alignment: 8, callee-saved-register: '$r13' } + - { id: 3, type: spill-slot, offset: -32, size: 8, alignment: 16, callee-saved-register: '$r14' } + - { id: 4, type: spill-slot, offset: -24, size: 8, alignment: 8, callee-saved-register: '$r15' } + - { id: 5, type: spill-slot, offset: -16, size: 8, alignment: 16, callee-saved-register: '$rbp' } +stack: + - { id: 0, type: spill-slot, offset: -64, size: 8, alignment: 8 } +machineFunctionInfo: {} +body: | + bb.0.entry: + liveins: $rdi, $rdx, $rsi, $rbp, $r15, $r14, $r13, $r12, $rbx + + ; CHECK-LABEL: bb.0.entry: + + ;; PART ONE: For this first block of tests, check that information about + ;; the size of the value on the stack makes it through to the expression. + + $al = MOV8ri 0, debug-instr-number 1, debug-location !7 + DBG_INSTR_REF 1, 0, !8, !DIExpression(DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value), debug-location !7 + ; CHECK: DBG_VALUE $al, $noreg, ![[VARNUM]], + ; CHECK-SAME: !DIExpression(DW_OP_LLVM_convert, 8, DW_ATE_signed, + ; CHECK-SAME : DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value) + + MOV8mr $rsp, 1, $noreg, -8, $noreg, renamable $al :: (store 1 into %stack.0) + ;; Clobber to force variable location onto stack. We should use a + ;; deref_size 1 because the value is smaller than the variable. + $al = MOV8ri 0, debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, + ; CHECK-SAME: DW_OP_deref_size, 1, DW_OP_LLVM_convert, 8, DW_ATE_signed, + ; CHECK-SAME: DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value), + + ; Terminate the current variable location, + DBG_VALUE $noreg, $noreg, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg, ![[VARNUM]], !DIExpression() + + ;; Try again, but with the value originating on the stack, to ensure that + ;; we can find its size. It should be deref_size 1 again. + INC8m $rsp, 1, $noreg, 4, $noreg, implicit-def dead $eflags, debug-instr-number 2, debug-location !7 :: (store (s8) into %stack.0) + DBG_INSTR_REF 2, 1000000, !8, !DIExpression(DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, + ; CHECK-SAME: DW_OP_deref_size, 1, DW_OP_LLVM_convert, 8, DW_ATE_signed, + ; CHECK-SAME: DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value), + + $eax = MOV32ri 0, debug-location !7 + + ;; How about DBG_PHIs? The size of the value is communicated by the register + ;; size, or an extra DBG_PHI operand, and that should feed through into the + ;; decision of whether to deref_size or not. + + DBG_VALUE $noreg, $noreg, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $noreg + + $al = MOV8ri 0, debug-location !7 + DBG_PHI $al, 7 + + MOV32mr $rsp, 1, $noreg, -8, $noreg, renamable $eax :: (store 4 into %stack.0) + $eax = MOV32ri 0, debug-location !7 + DBG_INSTR_REF 7, 0, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref_size, 1, DW_OP_stack_value) + + $eax = MOV32ri 0, debug-location !7 + + ;; And for when the DBG_PHI specifies a stack size... + DBG_PHI %stack.0, 8, 16 + DBG_INSTR_REF 8, 0, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref_size, 2, DW_OP_stack_value) + + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + DBG_VALUE $noreg, $noreg, !10, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + ;; PART TWO: test the twelve kinds of location expression mentioned in the + ;; opening comment of this test, in that order. Please update the opening + ;; comment, these lines and the DWARF check lines at the same time. + ;; In each test we'll state a location, give it an instruction to cover, + ;; and then terminate it. + + ;; Scalar (32), Large value (64), no stack value, + $rax = MOV64ri 0, debug-instr-number 10, debug-location !7 + MOV64mr $rsp, 1, $noreg, -8, $noreg, renamable $rax :: (store 8 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 10, 0, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref_size, 8, DW_OP_stack_value), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + ;; Scalar (32), Same value size (32), no stack value, + $eax = MOV32ri 0, debug-instr-number 11, debug-location !7 + MOV32mr $rsp, 1, $noreg, -8, $noreg, renamable $eax :: (store 4 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 11, 0, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $rsp, 0, ![[VARNUM]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + ;; Scalar (32), Smaller value (8), no stack value, + $al = MOV8ri 0, debug-instr-number 12, debug-location !7 + MOV8mr $rsp, 1, $noreg, -8, $noreg, renamable $al :: (store 1 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 12, 0, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref_size, 1, DW_OP_stack_value), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + ;; Fragment (32), Larger value (64), no stack value, + $rax = MOV64ri 0, debug-instr-number 13, debug-location !7 + MOV64mr $rsp, 1, $noreg, -8, $noreg, renamable $rax :: (store 8 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 13, 0, !10, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM2]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref_size, 8, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 32), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !10, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + ;; Fragment (32), Same value size (32), no stack value, + $eax = MOV32ri 0, debug-instr-number 14, debug-location !7 + MOV32mr $rsp, 1, $noreg, -8, $noreg, renamable $eax :: (store 4 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 14, 0, !10, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $rsp, 0, ![[VARNUM2]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_LLVM_fragment, 0, 32), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !10, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + ;; Fragment (32), Smaller value (8), no stack value, + $al = MOV8ri 0, debug-instr-number 15, debug-location !7 + MOV8mr $rsp, 1, $noreg, -8, $noreg, renamable $al :: (store 1 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 15, 0, !10, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM2]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref_size, 1, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 32), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !10, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + + + ;; Scalar (32), Large value (64), with stack value, + $rax = MOV64ri 0, debug-instr-number 16, debug-location !7 + MOV64mr $rsp, 1, $noreg, -8, $noreg, renamable $rax :: (store 8 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 16, 0, !8, !DIExpression(DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref_size, 8, DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + ;; Scalar (32), Same value size (32), no stack value, + $eax = MOV32ri 0, debug-instr-number 17, debug-location !7 + MOV32mr $rsp, 1, $noreg, -8, $noreg, renamable $eax :: (store 4 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 17, 0, !8, !DIExpression(DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref, DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + ;; Scalar (32), Smaller value (8), no stack value, + $al = MOV8ri 0, debug-instr-number 18, debug-location !7 + MOV8mr $rsp, 1, $noreg, -8, $noreg, renamable $al :: (store 1 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 18, 0, !8, !DIExpression(DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref_size, 1, DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !8, !DIExpression(), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + ;; Fragment (32), Larger value (64), no stack value, + $rax = MOV64ri 0, debug-instr-number 19, debug-location !7 + MOV64mr $rsp, 1, $noreg, -8, $noreg, renamable $rax :: (store 8 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 19, 0, !10, !DIExpression(DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM2]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref_size, 8, DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 32), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !10, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + ;; Fragment (32), Same value size (32), no stack value, + $eax = MOV32ri 0, debug-instr-number 20, debug-location !7 + MOV32mr $rsp, 1, $noreg, -8, $noreg, renamable $eax :: (store 4 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 20, 0, !10, !DIExpression(DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM2]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref_size, 4, DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 32), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !10, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + ;; Fragment (32), Smaller value (8), no stack value, + $al = MOV8ri 0, debug-instr-number 21, debug-location !7 + MOV8mr $rsp, 1, $noreg, -8, $noreg, renamable $al :: (store 1 into %stack.0) + $rax = MOV64ri 0, debug-location !7 + DBG_INSTR_REF 21, 0, !10, !DIExpression(DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $rsp, $noreg, ![[VARNUM2]], + ; CHECK-SAME: !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref_size, 1, DW_OP_constu, 1, DW_OP_plus, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 32), + $eax = MOV32ri 0, debug-location !7 + DBG_VALUE $noreg, $noreg, !10, !DIExpression(DW_OP_LLVM_fragment, 0, 32), debug-location !7 + ; CHECK: DBG_VALUE $noreg, $noreg + + RET64 0, debug-location !7 +... diff --git a/llvm/test/DebugInfo/MIR/InstrRef/follow-spill-of-indir-value.mir b/llvm/test/DebugInfo/MIR/InstrRef/follow-spill-of-indir-value.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/InstrRef/follow-spill-of-indir-value.mir @@ -0,0 +1,143 @@ +# RUN: llc %s -o - -experimental-debug-variable-locations=true \ +# RUN: -run-pass=livedebugvalues \ +# RUN: | FileCheck %s --implicit-check-not=DBG_VALUE +# RUN: llc %s -o - -experimental-debug-variable-locations=true \ +# RUN: -start-before=livedebugvalues -filetype=obj \ +# RUN: | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF + +## Test that we can spill indirect plain variable locations, and those with +## non-empty expressions, to the stack. And that the derefs go in the right +## place. + +## Capture variable num, +# CHECK-DAG: ![[VARNUM:[0-9]+]] = !DILocalVariable(name: "nt", + +# DWARF: DW_TAG_formal_parameter +# DWARF-NEXT: DW_AT_location +# DWARF-NEXT: DW_OP_breg5 RDI+0 +## +## First location: variable pointed to by basereg $rdi +## +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref +## +## Spilt to stack: push stack address and deref, variable pointed to by the +## pointer loaded off the stack. +## +# DWARF-NEXT: DW_OP_breg5 RDI+8 +## +## Second location, variable pointed to by the register value plus eight. +## +# DWARF-NEXT: DW_OP_breg7 RSP-8, DW_OP_deref, DW_OP_lit8, DW_OP_plus) +## +## Spilt to stack: push stack location and deref the pointer onto the dwarf +## expr stack. Then add eight to it, and it points to the variable. +## + + +--- | + source_filename = "t.cpp" + 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--linux" + + %struct.NonTrivial = type { i32 } + + ; Function Attrs: nounwind uwtable + define i32 @_Z3foo10NonTrivial(%struct.NonTrivial* nocapture readonly %nt) local_unnamed_addr #0 !dbg !7 { + entry: + tail call void @llvm.dbg.declare(metadata %struct.NonTrivial* %nt, metadata !20, metadata !DIExpression()), !dbg !21 + tail call void asm sideeffect "", "~{rax},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{rbp},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{dirflag},~{fpsr},~{flags}"() #2, !dbg !22, !srcloc !23 + %i1 = bitcast %struct.NonTrivial* %nt to i32*, !dbg !24 + %0 = load i32, i32* %i1, align 4, !dbg !24, !tbaa !25 + ret i32 %0, !dbg !30 + } + + ; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn + declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + + attributes #0 = { nounwind uwtable } + attributes #1 = { nocallback nofree nosync nounwind readnone speculatable willreturn } + attributes #2 = { nounwind } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 6.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) + !1 = !DIFile(filename: "t.cpp", directory: "C:\\src\\llvm-project\\build") + !2 = !{} + !3 = !{i32 2, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 6.0.0 "} + !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foo10NonTrivial", scope: !1, file: !1, line: 10, type: !8, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !19) + !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: "NonTrivial", file: !1, line: 5, size: 32, elements: !12, identifier: "_ZTS10NonTrivial") + !12 = !{!13, !14, !18} + !13 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !11, file: !1, line: 8, baseType: !10, size: 32) + !14 = !DISubprogram(name: "NonTrivial", scope: !11, file: !1, line: 6, type: !15, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) + !15 = !DISubroutineType(types: !16) + !16 = !{null, !17} + !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) + !18 = !DISubprogram(name: "~NonTrivial", scope: !11, file: !1, line: 7, type: !15, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) + !19 = !{!20} + !20 = !DILocalVariable(name: "nt", arg: 1, scope: !7, file: !1, line: 10, type: !11) + !21 = !DILocation(line: 10, column: 20, scope: !7) + !22 = !DILocation(line: 11, column: 3, scope: !7) + !23 = !{i32 -2147471481} + !24 = !DILocation(line: 12, column: 13, scope: !7) + !25 = !{!26, !27, i64 0} + !26 = !{!"_ZTS10NonTrivial", !27, i64 0} + !27 = !{!"int", !28, i64 0} + !28 = !{!"omnipotent char", !29, i64 0} + !29 = !{!"Simple C++ TBAA"} + !30 = !DILocation(line: 12, column: 3, scope: !7) + +... +--- +name: _Z3foo10NonTrivial +alignment: 16 +tracksRegLiveness: true +tracksDebugUserValues: true +liveins: + - { reg: '$rdi' } +frameInfo: + stackSize: 48 + offsetAdjustment: -48 + maxAlignment: 8 + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 48 +fixedStack: + - { id: 0, type: spill-slot, offset: -56, size: 8, alignment: 8, callee-saved-register: '$rbx' } + - { id: 1, type: spill-slot, offset: -48, size: 8, alignment: 16, callee-saved-register: '$r12' } + - { id: 2, type: spill-slot, offset: -40, size: 8, alignment: 8, callee-saved-register: '$r13' } + - { id: 3, type: spill-slot, offset: -32, size: 8, alignment: 16, callee-saved-register: '$r14' } + - { id: 4, type: spill-slot, offset: -24, size: 8, alignment: 8, callee-saved-register: '$r15' } + - { id: 5, type: spill-slot, offset: -16, size: 8, alignment: 16, callee-saved-register: '$rbp' } +stack: + - { id: 0, type: spill-slot, offset: -64, size: 8, alignment: 8 } +machineFunctionInfo: {} +body: | + bb.0.entry: + liveins: $rdi, $rbp, $r15, $r14, $r13, $r12, $rbx + + DBG_VALUE $rdi, 0, !20, !DIExpression(), debug-location !21 + ; CHECK: DBG_VALUE $rdi, + MOV64mr $rsp, 1, $noreg, -8, $noreg, $rdi :: (store (s64) into %stack.0) + $rdi = MOV64ri 0 + ; CHECK: DBG_VALUE $rsp, 0, ![[VARNUM]], !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref), + + $rdi = MOV64ri 0 ; dummy instr for the above DBG_VALUE to cover in a loclist + + DBG_VALUE $rdi, 0, !20, !DIExpression(DW_OP_constu, 8, DW_OP_plus), debug-location !21 + ; CHECK: DBG_VALUE $rdi, + MOV64mr $rsp, 1, $noreg, -8, $noreg, $rdi :: (store (s64) into %stack.0) + $rdi = MOV64ri 0 + ; CHECK: DBG_VALUE $rsp, 0, ![[VARNUM]], !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref, DW_OP_constu, 8, DW_OP_plus), + + renamable $rax = MOV64rm $rsp, 1, $noreg, -8, $noreg :: (load (s64) from %stack.0) + renamable $eax = MOV32rm killed renamable $rax, 1, $noreg, 0, $noreg, debug-location !24 :: (load (s32) from %ir.i1, !tbaa !25) + RET64 $eax, debug-location !30 + +... diff --git a/llvm/test/DebugInfo/MIR/X86/live-debug-values-restore.mir b/llvm/test/DebugInfo/MIR/X86/live-debug-values-restore.mir --- a/llvm/test/DebugInfo/MIR/X86/live-debug-values-restore.mir +++ b/llvm/test/DebugInfo/MIR/X86/live-debug-values-restore.mir @@ -414,15 +414,12 @@ # This third function tests that complex expressions are spilt, and restored # correctly within a basic block. -# FIXME: the spilt location below is wrong, there should be a deref between -# the spill-offset and the DW_OP_plus_uconst. - # CHECK-LABEL: name: h # CHECK-LABEL: bb.0.entry: # CHECK: DBG_VALUE $rdi, $noreg, ![[RVAR]], !DIExpression(DW_OP_plus_uconst, 1) # CHECK-LABEL: bb.1.if.then: # CHECK: DBG_VALUE $rdi, $noreg, ![[RVAR]], !DIExpression(DW_OP_plus_uconst, 1) -# CHECK: DBG_VALUE $rsp, 0, ![[RVAR]], !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_plus_uconst, 1) +# CHECK: DBG_VALUE $rsp, $noreg, ![[RVAR]], !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref, DW_OP_plus_uconst, 1) # CHECK: DBG_VALUE $rdi, $noreg, ![[RVAR]], !DIExpression(DW_OP_plus_uconst, 1) # CHECK-LABEL: bb.2.if.end: # CHECK: DBG_VALUE $rdi, $noreg, ![[RVAR]], !DIExpression(DW_OP_plus_uconst, 1) @@ -527,8 +524,6 @@ # bb 2 and 3, neither of which modifies the stack loc. The exit block (3) # should still be tracking the spill, and restore it on stack load. -# FIXME: this test too contains a broken spill location. - # Summary: loc is in $rdi in bb0, spills to stack in bb1, remains in stack # in bb2, starts in stack then loaded in bb3. @@ -537,11 +532,11 @@ # CHECK: DBG_VALUE $rdi, $noreg, ![[SVAR]], !DIExpression(DW_OP_plus_uconst, 1) # CHECK-LABEL: bb.1.foo: # CHECK: DBG_VALUE $rdi, $noreg, ![[SVAR]], !DIExpression(DW_OP_plus_uconst, 1) -# CHECK: DBG_VALUE $rsp, 0, ![[SVAR]], !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_plus_uconst, 1) +# CHECK: DBG_VALUE $rsp, $noreg, ![[SVAR]], !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref, DW_OP_plus_uconst, 1) # CHECK-LABEL: bb.2.if.then: -# CHECK: DBG_VALUE $rsp, 0, ![[SVAR]], !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_plus_uconst, 1) +# CHECK: DBG_VALUE $rsp, $noreg, ![[SVAR]], !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref, DW_OP_plus_uconst, 1) # CHECK-LABEL: bb.3.if.end -# CHECK: DBG_VALUE $rsp, 0, ![[SVAR]], !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_plus_uconst, 1) +# CHECK: DBG_VALUE $rsp, $noreg, ![[SVAR]], !DIExpression(DW_OP_constu, 8, DW_OP_minus, DW_OP_deref, DW_OP_plus_uconst, 1) # CHECK: DBG_VALUE $rdi, $noreg, ![[SVAR]], !DIExpression(DW_OP_plus_uconst, 1) name: i alignment: 16 diff --git a/llvm/test/DebugInfo/MIR/X86/livedebugvalues_load_in_loop.mir b/llvm/test/DebugInfo/MIR/X86/livedebugvalues_load_in_loop.mir --- a/llvm/test/DebugInfo/MIR/X86/livedebugvalues_load_in_loop.mir +++ b/llvm/test/DebugInfo/MIR/X86/livedebugvalues_load_in_loop.mir @@ -52,7 +52,7 @@ !3 = !{!4} !4 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression()) !5 = distinct !DIGlobalVariable(name: "start", scope: !0, file: !1, line: 4, type: !6, isLocal: false, isDefinition: true) - !6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !6 = !DIBasicType(name: "int", size: 64, encoding: DW_ATE_signed) !7 = !{i32 2, !"Dwarf Version", i32 4} !8 = !{i32 2, !"Debug Info Version", i32 3} !9 = !{i32 1, !"wchar_size", i32 2}