diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -4793,17 +4793,24 @@ of the stack is treated as an address. The second stack entry is treated as an address space identifier. - ``DW_OP_stack_value`` marks a constant value. -- If an expression is marked with ``DW_OP_entry_value`` all register and - memory read operations refer to the respective value at the function entry. - The first operand of ``DW_OP_entry_value`` is the size of following - DWARF expression. - ``DW_OP_entry_value`` may appear after the ``LiveDebugValues`` pass. - LLVM only supports entry values for function parameters - that are unmodified throughout a function and that are described as - simple register location descriptions. - ``DW_OP_entry_value`` may also appear after the ``AsmPrinter`` pass when - a call site parameter value (``DW_AT_call_site_parameter_value``) - is represented as entry value of the parameter. +- ``DW_OP_LLVM_entry_value, N`` can only appear at the beginning of a + ``DIExpression``, and it specifies that all register and memory read + operations for the debug value instruction's value/address operand and for + the ``(N - 1)`` operations immediately following the + ``DW_OP_LLVM_entry_value`` refer to their respective values at function + entry. For example, ``!DIExpression(DW_OP_LLVM_entry_value, 1, + DW_OP_plus_uconst, 123, DW_OP_stack_value)`` specifies an expression where + the entry value of the debug value instruction's value/address operand is + pushed to the stack, and is added with 123. Due to framework limitations + ``N`` can currently only be 1. + + ``DW_OP_LLVM_entry_value`` is only legal in MIR. The operation is introduced + by the ``LiveDebugValues`` pass; currently only for function parameters that + are unmodified throughout a function and that are described as simple + register location descriptions. The operation is also introduced by the + ``AsmPrinter`` pass when a call site parameter value + (``DW_AT_call_site_parameter_value``) is represented as entry value of the + parameter. - ``DW_OP_breg`` (or ``DW_OP_bregx``) represents a content on the provided signed offset of the specified register. The opcode is only generated by the ``AsmPrinter`` pass to describe call site parameter value which requires an @@ -4852,7 +4859,7 @@ The `ArgumentNotModified` flag marks a function argument whose value is not modified throughout of a function. This flag is used to decide -whether a DW_OP_entry_value can be used in a location description +whether a DW_OP_LLVM_entry_value can be used in a location description after the function prologue. The language frontend is expected to compute this property for each DILocalVariable. The flag should be used only in optimized code. diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.h b/llvm/include/llvm/BinaryFormat/Dwarf.h --- a/llvm/include/llvm/BinaryFormat/Dwarf.h +++ b/llvm/include/llvm/BinaryFormat/Dwarf.h @@ -117,9 +117,10 @@ #include "llvm/BinaryFormat/Dwarf.def" DW_OP_lo_user = 0xe0, DW_OP_hi_user = 0xff, - DW_OP_LLVM_fragment = 0x1000, ///< Only used in LLVM metadata. - DW_OP_LLVM_convert = 0x1001, ///< Only used in LLVM metadata. - DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata. + DW_OP_LLVM_fragment = 0x1000, ///< Only used in LLVM metadata. + DW_OP_LLVM_convert = 0x1001, ///< Only used in LLVM metadata. + DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata. + DW_OP_LLVM_entry_value = 0x1003, ///< Only used in LLVM metadata. }; enum TypeKind : uint8_t { diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h --- a/llvm/include/llvm/CodeGen/MachineInstr.h +++ b/llvm/include/llvm/CodeGen/MachineInstr.h @@ -1042,7 +1042,7 @@ } /// A DBG_VALUE is an entry value iff its debug expression contains the - /// DW_OP_entry_value DWARF operation. + /// DW_OP_LLVM_entry_value operation. bool isDebugEntryValue() const { return isDebugValue() && getDebugExpression()->isEntryValue(); } diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -2569,7 +2569,7 @@ /// (This is the only configuration of entry values that is supported.) bool isEntryValue() const { return getNumElements() > 0 && - getElement(0) == dwarf::DW_OP_entry_value; + getElement(0) == dwarf::DW_OP_LLVM_entry_value; } }; diff --git a/llvm/lib/BinaryFormat/Dwarf.cpp b/llvm/lib/BinaryFormat/Dwarf.cpp --- a/llvm/lib/BinaryFormat/Dwarf.cpp +++ b/llvm/lib/BinaryFormat/Dwarf.cpp @@ -149,6 +149,8 @@ return "DW_OP_LLVM_fragment"; case DW_OP_LLVM_tag_offset: return "DW_OP_LLVM_tag_offset"; + case DW_OP_LLVM_entry_value: + return "DW_OP_LLVM_entry_value"; } } @@ -160,6 +162,7 @@ .Case("DW_OP_LLVM_convert", DW_OP_LLVM_convert) .Case("DW_OP_LLVM_fragment", DW_OP_LLVM_fragment) .Case("DW_OP_LLVM_tag_offset", DW_OP_LLVM_tag_offset) + .Case("DW_OP_LLVM_entry_value", DW_OP_LLVM_entry_value) .Default(0); } diff --git a/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp b/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp @@ -41,7 +41,7 @@ static Register isDescribedByReg(const MachineInstr &MI) { assert(MI.isDebugValue()); assert(MI.getNumOperands() == 4); - // If the location of variable is an entry value (DW_OP_entry_value) + // If the location of variable is an entry value (DW_OP_LLVM_entry_value) // do not consider it as a register location. if (MI.getDebugExpression()->isEntryValue()) return 0; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -1205,7 +1205,7 @@ if (DIExpr->isEntryValue()) { DwarfExpr.setEntryValueFlag(); - DwarfExpr.addEntryValueExpression(Cursor); + DwarfExpr.beginEntryValueExpression(Cursor); } const TargetRegisterInfo &TRI = *Asm->MF->getSubtarget().getRegisterInfo(); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -669,10 +669,9 @@ // Emit the call site parameter's value as an entry value. if (ShouldTryEmitEntryVals) { - // Create an entry value expression where the expression following - // the 'DW_OP_entry_value' will be the size of 1 (a register operation). - DIExpression *EntryExpr = DIExpression::get(MF->getFunction().getContext(), - {dwarf::DW_OP_entry_value, 1}); + // Create an expression where the register's entry value is used. + DIExpression *EntryExpr = DIExpression::get( + MF->getFunction().getContext(), {dwarf::DW_OP_LLVM_entry_value, 1}); for (auto RegEntry : ForwardedRegWorklist) { unsigned FwdReg = RegEntry; auto EntryValReg = RegsForEntryValues.find(RegEntry); @@ -2200,7 +2199,7 @@ if (DIExpr->isEntryValue()) { DwarfExpr.setEntryValueFlag(); - DwarfExpr.addEntryValueExpression(Cursor); + DwarfExpr.beginEntryValueExpression(Cursor); } const TargetRegisterInfo &TRI = *AP.MF->getSubtarget().getRegisterInfo(); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h @@ -111,6 +111,9 @@ const char *Comment; }; + /// Whether we are currently emitting an entry value operation. + bool IsEmittingEntryValue = false; + DwarfCompileUnit &CU; /// The register location, if any. @@ -260,6 +263,10 @@ /// expression. See PR21176 for more details. void addStackValue(); + /// Finalize an entry value by emitting its size operand, and committing the + /// DWARF block which has been emitted to the temporary buffer. + void finalizeEntryValue(); + ~DwarfExpression() = default; public: @@ -310,8 +317,11 @@ DIExpressionCursor &Expr, unsigned MachineReg, unsigned FragmentOffsetInBits = 0); - /// Emit entry value dwarf operation. - void addEntryValueExpression(DIExpressionCursor &ExprCursor); + /// Begin emission of an entry value dwarf operation. The entry value's + /// first operand is the size of the DWARF block (its second operand), + /// which needs to be calculated at time of emission, so we don't emit + /// any operands here. + void beginEntryValueExpression(DIExpressionCursor &ExprCursor); /// Emit all remaining operations in the DIExpressionCursor. /// diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -254,6 +254,9 @@ addOpPiece(Reg.Size); } + if (isEntryValue()) + finalizeEntryValue(); + if (isEntryValue() && !isParameterValue() && DwarfVersion >= 4) emitOp(dwarf::DW_OP_stack_value); @@ -313,14 +316,33 @@ return true; } -void DwarfExpression::addEntryValueExpression(DIExpressionCursor &ExprCursor) { +void DwarfExpression::beginEntryValueExpression( + DIExpressionCursor &ExprCursor) { auto Op = ExprCursor.take(); - assert(Op && Op->getOp() == dwarf::DW_OP_entry_value); + assert(Op && Op->getOp() == dwarf::DW_OP_LLVM_entry_value); assert(!isMemoryLocation() && "We don't support entry values of memory locations yet"); + assert(!IsEmittingEntryValue && "Already emitting entry value?"); + assert(Op->getArg(0) == 1 && + "Can currently only emit entry values covering a single operation"); emitOp(CU.getDwarf5OrGNULocationAtom(dwarf::DW_OP_entry_value)); - emitUnsigned(Op->getArg(0)); + IsEmittingEntryValue = true; + enableTemporaryBuffer(); +} + +void DwarfExpression::finalizeEntryValue() { + assert(IsEmittingEntryValue && "Entry value not open?"); + disableTemporaryBuffer(); + + // Emit the entry value's size operand. + unsigned Size = getTemporaryBufferSize(); + emitUnsigned(Size); + + // Emit the entry value's DWARF block operand. + commitTemporaryBuffer(); + + IsEmittingEntryValue = false; } /// Assuming a well-formed expression, match "DW_OP_deref* DW_OP_LLVM_fragment?". diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -843,7 +843,7 @@ case dwarf::DW_OP_deref_size: case dwarf::DW_OP_plus_uconst: case dwarf::DW_OP_LLVM_tag_offset: - case dwarf::DW_OP_entry_value: + case dwarf::DW_OP_LLVM_entry_value: case dwarf::DW_OP_regx: return 2; default: @@ -890,10 +890,12 @@ return false; break; } - case dwarf::DW_OP_entry_value: { - // An entry value operator must appear at the begin and the size - // of following expression should be 1, because we support only - // entry values of a simple register location. + case dwarf::DW_OP_LLVM_entry_value: { + // An entry value operator must appear at the beginning and the number of + // operations it cover can currently only be 1, because we support only + // entry values of a simple register location. One reason for this is that + // we currently can't calculate the size of the resulting DWARF block for + // other expressions. return I->get() == expr_op_begin()->get() && I->getArg(0) == 1 && getNumElements() == 2; } @@ -1050,7 +1052,7 @@ assert(Expr && "Can't prepend ops to this expression"); if (EntryValue) { - Ops.push_back(dwarf::DW_OP_entry_value); + Ops.push_back(dwarf::DW_OP_LLVM_entry_value); // Add size info needed for entry value expression. // Add plus one for target register operand. Ops.push_back(Expr->getNumElements() + 1); diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -518,6 +518,7 @@ DIExpression::FragmentInfo Fragment, ValueOrMetadata *Desc); void verifyFnArgs(const DbgVariableIntrinsic &I); + void verifyNotEntryValue(const DbgVariableIntrinsic &I); /// Module-level debug info verification... void verifyCompileUnits(); @@ -4213,8 +4214,10 @@ visitMDNode(*N); } - if (auto *DII = dyn_cast(&I)) + if (auto *DII = dyn_cast(&I)) { verifyFragmentExpression(*DII); + verifyNotEntryValue(*DII); + } InstsInThisBlock.insert(&I); } @@ -5047,6 +5050,16 @@ Prev, Var); } +void Verifier::verifyNotEntryValue(const DbgVariableIntrinsic &I) { + DIExpression *E = dyn_cast_or_null(I.getRawExpression()); + + // We don't know whether this intrinsic verified correctly. + if (!E || !E->isValid()) + return; + + AssertDI(!E->isEntryValue(), "Entry values are only allowed in MIR", &I); +} + void Verifier::verifyCompileUnits() { // When more than one Module is imported into the same context, such as during // an LTO build before linking the modules, ODR type uniquing may cause types diff --git a/llvm/test/DebugInfo/ARM/entry-value-multi-byte-expr.ll b/llvm/test/DebugInfo/ARM/entry-value-multi-byte-expr.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/ARM/entry-value-multi-byte-expr.ll @@ -0,0 +1,91 @@ +; RUN: llc -debug-entry-values -filetype=asm -o - %s | FileCheck %s + +; Verify that the size operands of the DW_OP_GNU_entry_value operations are +; correct for the multi-byte DW_OP_regx expressions. + +; Based on the following C reproducer: +; +; extern void clobber(); +; double global; +; int f(double a, double b) { +; global = a + b; +; clobber(); +; return 1; +; } + +; This test checks the assembly output rather than the output from +; llvm-dwarfdump, as the latter printed the DW_OP_regx D0 correctly, even +; though the entry value's size operand did not fully cover that: + +; DW_OP_GNU_entry_value(DW_OP_regx D0), DW_OP_stack_value +; +; whereas readelf interpreted it as an DW_OP_GNU_entry_value covering one byte, +; resulting in garbage data: +; +; DW_OP_GNU_entry_value: (DW_OP_regx: 0 (r0)); DW_OP_breg16 (r16): 2; DW_OP_stack_value + +; CHECK: .byte 243 @ DW_OP_GNU_entry_value +; CHECK-NEXT: .byte 3 @ 3 +; CHECK-NEXT: .byte 144 @ DW_OP_regx +; CHECK-NEXT: .byte 128 @ 256 +; CHECK-NEXT: .byte 2 @ +; CHECK-NEXT: .byte 159 @ DW_OP_stack_value + +; CHECK: .byte 243 @ DW_OP_GNU_entry_value +; CHECK-NEXT: .byte 3 @ 3 +; CHECK-NEXT: .byte 144 @ DW_OP_regx +; CHECK-NEXT: .byte 129 @ 257 +; CHECK-NEXT: .byte 2 @ +; CHECK-NEXT: .byte 159 @ DW_OP_stack_value + +target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "armv7-unknown-unknown" + +@global = common global double 0.000000e+00, align 8, !dbg !0 + +; Function Attrs: nounwind +define arm_aapcs_vfpcc i32 @f(double %a, double %b) #0 !dbg !12 { +entry: + call void @llvm.dbg.value(metadata double %a, metadata !17, metadata !DIExpression()), !dbg !19 + call void @llvm.dbg.value(metadata double %b, metadata !18, metadata !DIExpression()), !dbg !19 + %add = fadd double %a, %b, !dbg !20 + store double %add, double* @global, align 8, !dbg !20 + tail call arm_aapcs_vfpcc void @clobber(), !dbg !21 + ret i32 1, !dbg !22 +} + +declare arm_aapcs_vfpcc void @clobber() + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { nounwind } +attributes #1 = { nounwind readnone speculatable willreturn } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "global", scope: !2, file: !3, line: 4, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "entry-value-multi-byte-expr.c", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{i32 1, !"min_enum_size", i32 4} +!11 = !{!"clang version 10.0.0"} +!12 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 6, type: !13, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !16) +!13 = !DISubroutineType(types: !14) +!14 = !{!15, !6, !6} +!15 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!16 = !{!17, !18} +!17 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !3, line: 6, type: !6, flags: DIFlagArgumentNotModified) +!18 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !3, line: 6, type: !6, flags: DIFlagArgumentNotModified) +!19 = !DILocation(line: 0, scope: !12) +!20 = !DILocation(line: 7, scope: !12) +!21 = !DILocation(line: 8, scope: !12) +!22 = !DILocation(line: 9, scope: !12) diff --git a/llvm/test/DebugInfo/MIR/Hexagon/live-debug-values-bundled-entry-values.mir b/llvm/test/DebugInfo/MIR/Hexagon/live-debug-values-bundled-entry-values.mir --- a/llvm/test/DebugInfo/MIR/Hexagon/live-debug-values-bundled-entry-values.mir +++ b/llvm/test/DebugInfo/MIR/Hexagon/live-debug-values-bundled-entry-values.mir @@ -98,7 +98,7 @@ # CHECK-NEXT: $r29 = S2_allocframe $r29, 0 # CHECK-NEXT: J2_call @clobber # CHECK-NEXT: } -# CHECK-NEXT: DBG_VALUE $r0, $noreg, !16, !DIExpression(DW_OP_entry_value, 1) +# CHECK-NEXT: DBG_VALUE $r0, $noreg, !16, !DIExpression(DW_OP_LLVM_entry_value, 1) --- name: bar @@ -136,4 +136,4 @@ # CHECK-NEXT: $r29 = S2_allocframe $r29, 0 # CHECK-NEXT: J2_call @clobber # CHECK-NEXT: } -# CHECK-NEXT: DBG_VALUE $r0, $noreg, !22, !DIExpression(DW_OP_entry_value, 1) +# CHECK-NEXT: DBG_VALUE $r0, $noreg, !22, !DIExpression(DW_OP_LLVM_entry_value, 1) diff --git a/llvm/test/DebugInfo/MIR/X86/avoid-single-entry-value-location.mir b/llvm/test/DebugInfo/MIR/X86/avoid-single-entry-value-location.mir --- a/llvm/test/DebugInfo/MIR/X86/avoid-single-entry-value-location.mir +++ b/llvm/test/DebugInfo/MIR/X86/avoid-single-entry-value-location.mir @@ -61,7 +61,7 @@ DBG_VALUE $edi, $noreg, !16, !DIExpression(), debug-location !18 DBG_VALUE $edi, $noreg, !17, !DIExpression(), debug-location !18 $edi = KILL renamable $edi, implicit killed $rdi, debug-location !18 - DBG_VALUE $rdi, $noreg, !16, !DIExpression(DW_OP_entry_value, 1), debug-location !18 + DBG_VALUE $rdi, $noreg, !16, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !18 TAILJMPd64 @fn2, csr_64, implicit $rsp, implicit $ssp, implicit $rsp, implicit $ssp, implicit killed $edi, debug-location !18 ... diff --git a/llvm/test/DebugInfo/MIR/X86/dbgcall-site-interpretation.mir b/llvm/test/DebugInfo/MIR/X86/dbgcall-site-interpretation.mir --- a/llvm/test/DebugInfo/MIR/X86/dbgcall-site-interpretation.mir +++ b/llvm/test/DebugInfo/MIR/X86/dbgcall-site-interpretation.mir @@ -190,10 +190,10 @@ $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !21 CFI_INSTRUCTION def_cfa_offset 24, debug-location !21 $r14 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !21 - DBG_VALUE $ecx, $noreg, !17, !DIExpression(DW_OP_entry_value, 1), debug-location !21 + DBG_VALUE $ecx, $noreg, !17, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !21 CFI_INSTRUCTION def_cfa_offset 16, debug-location !21 $r15 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !21 - DBG_VALUE $esi, $noreg, !15, !DIExpression(DW_OP_entry_value, 1), debug-location !21 + DBG_VALUE $esi, $noreg, !15, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location !21 CFI_INSTRUCTION def_cfa_offset 8, debug-location !21 RETQ $eax, debug-location !21 diff --git a/llvm/test/DebugInfo/MIR/X86/dbginfo-entryvals.mir b/llvm/test/DebugInfo/MIR/X86/dbginfo-entryvals.mir --- a/llvm/test/DebugInfo/MIR/X86/dbginfo-entryvals.mir +++ b/llvm/test/DebugInfo/MIR/X86/dbginfo-entryvals.mir @@ -14,8 +14,8 @@ # fn2 (a); # u --; #} -# CHECK: DBG_VALUE $edi, $noreg, !14, !DIExpression(DW_OP_entry_value, 1), debug-location {{.*}} -# CHECK: DBG_VALUE $esi, $noreg, !15, !DIExpression(DW_OP_entry_value, 1), debug-location {{.*}} +# CHECK: DBG_VALUE $edi, $noreg, !14, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location {{.*}} +# CHECK: DBG_VALUE $esi, $noreg, !15, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location {{.*}} --- | ; ModuleID = 'test.c' diff --git a/llvm/test/DebugInfo/MIR/X86/multiple-param-dbg-value-entry.mir b/llvm/test/DebugInfo/MIR/X86/multiple-param-dbg-value-entry.mir --- a/llvm/test/DebugInfo/MIR/X86/multiple-param-dbg-value-entry.mir +++ b/llvm/test/DebugInfo/MIR/X86/multiple-param-dbg-value-entry.mir @@ -7,11 +7,11 @@ # return 123; #} # -# Verify that DW_OP_entry_values are generated for parameters with multiple +# Verify that DW_OP_LLVM_entry_values are generated for parameters with multiple # DBG_VALUEs at entry block. -# CHECK: DBG_VALUE $edi, $noreg, !{{.*}}, !DIExpression(DW_OP_entry_value, 1), debug-location {{.*}} -# CHECK: DBG_VALUE $edx, $noreg, !{{.*}}, !DIExpression(DW_OP_entry_value, 1), debug-location {{.*}} -# CHECK: DBG_VALUE $esi, $noreg, !{{.*}}, !DIExpression(DW_OP_entry_value, 1), debug-location {{.*}} +# CHECK: DBG_VALUE $edi, $noreg, !{{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location {{.*}} +# CHECK: DBG_VALUE $edx, $noreg, !{{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location {{.*}} +# CHECK: DBG_VALUE $esi, $noreg, !{{.*}}, !DIExpression(DW_OP_LLVM_entry_value, 1), debug-location {{.*}} --- | ; ModuleID = 'multiple-param-dbg-value-entry.ll' diff --git a/llvm/test/DebugInfo/Sparc/entry-value-complex-reg-expr.ll b/llvm/test/DebugInfo/Sparc/entry-value-complex-reg-expr.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Sparc/entry-value-complex-reg-expr.ll @@ -0,0 +1,79 @@ +; RUN: llc -debug-entry-values -filetype=asm -o - %s | FileCheck %s + +; Verify that the entry value covers both of the DW_OP_regx pieces. Previously +; the size operand of the entry value would be hardcoded to one. +; +; XXX: Is this really what should be emitted, or should we instead emit one +; entry value operation per DW_OP_regx? GDB can currently not understand +; entry values containing complex expressions like this. + +target datalayout = "E-m:e-i64:64-n32:64-S128" +target triple = "sparc64" + +; Based on the following C reproducer: +; +; volatile long double global; +; extern void clobber(); +; int foo(long double p) { +; global = p; +; clobber(); +; return 123; +; } + +; CHECK: .byte 243 ! DW_OP_GNU_entry_value +; CHECK-NEXT: .byte 8 ! 8 +; CHECK-NEXT: .byte 144 ! sub-register DW_OP_regx +; CHECK-NEXT: .byte 72 ! 72 +; CHECK-NEXT: .byte 147 ! DW_OP_piece +; CHECK-NEXT: .byte 8 ! 8 +; CHECK-NEXT: .byte 144 ! sub-register DW_OP_regx +; CHECK-NEXT: .byte 73 ! 73 +; CHECK-NEXT: .byte 147 ! DW_OP_piece +; CHECK-NEXT: .byte 8 ! 8 +; CHECK-NEXT: .byte 159 ! DW_OP_stack_value + +@global = common global fp128 0xL00000000000000000000000000000000, align 16, !dbg !0 + +; Function Attrs: nounwind +define signext i32 @foo(fp128 %p) #0 !dbg !12 { +entry: + call void @llvm.dbg.value(metadata fp128 %p, metadata !17, metadata !DIExpression()), !dbg !18 + store volatile fp128 %p, fp128* @global, align 16, !dbg !19 + tail call void @clobber(), !dbg !20 + ret i32 123, !dbg !21 +} + +declare void @clobber() + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { nounwind } +attributes #1 = { nounwind readnone speculatable willreturn } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "global", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "entry-value-complex-reg-expr.c", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "long double", size: 128, encoding: DW_ATE_float) +!8 = !{i32 2, !"Dwarf Version", i32 4} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 10.0.0"} +!12 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 3, type: !13, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !16) +!13 = !DISubroutineType(types: !14) +!14 = !{!15, !7} +!15 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!16 = !{!17} +!17 = !DILocalVariable(name: "p", arg: 1, scope: !12, file: !3, line: 3, type: !7, flags: DIFlagArgumentNotModified) +!18 = !DILocation(line: 0, scope: !12) +!19 = !DILocation(line: 4, scope: !12) +!20 = !DILocation(line: 5, scope: !12) +!21 = !DILocation(line: 6, scope: !12) diff --git a/llvm/test/Verifier/diexpression-dwarf-entry-value.ll b/llvm/test/Verifier/diexpression-dwarf-entry-value.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/diexpression-dwarf-entry-value.ll @@ -0,0 +1,8 @@ +; RUN: not opt -S < %s 2>&1 | FileCheck %s + +; We can only use the internal variant of the entry value operation, +; DW_OP_LLVM_entry_value, in DIExpressions. + +!named = !{!0} +; CHECK: invalid expression +!0 = !DIExpression(DW_OP_entry_value, 1) diff --git a/llvm/test/Verifier/diexpression-entry-value-llvm-ir.ll b/llvm/test/Verifier/diexpression-entry-value-llvm-ir.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/diexpression-entry-value-llvm-ir.ll @@ -0,0 +1,31 @@ +; RUN: llvm-as -disable-output <%s 2>&1| FileCheck %s + +; The DW_OP_LLVM_entry_value operation can only be used in MIR. + +; CHECK: Entry values are only allowed in MIR +; CHECK: call void @llvm.dbg.value(metadata i32 %param, metadata !{{.*}}, metadata !DIExpression(DW_OP_LLVM_entry_value, 1)) +; CHECK: warning: ignoring invalid debug info + +define void @foo(i32 %param) !dbg !4 { +entry: + call void @llvm.dbg.value(metadata i32 %param, metadata !8, metadata !DIExpression(DW_OP_LLVM_entry_value, 1)), !dbg !9 + ret void +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) #0 + +attributes #0 = { nounwind readnone speculatable willreturn } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, emissionKind: FullDebug) +!1 = !DIFile(filename: "a.c", directory: "/") +!2 = !{i32 2, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, type: !5, unit: !0) +!5 = !DISubroutineType(types: !6) +!6 = !{null, !7} +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !DILocalVariable(name: "param", arg: 1, scope: !4, file: !1, type: !7) +!9 = !DILocation(line: 0, scope: !4) diff --git a/llvm/test/Verifier/diexpression-entry-value.ll b/llvm/test/Verifier/diexpression-entry-value.ll --- a/llvm/test/Verifier/diexpression-entry-value.ll +++ b/llvm/test/Verifier/diexpression-entry-value.ll @@ -2,6 +2,11 @@ !named = !{!0, !1, !2} ; CHECK: invalid expression -!0 = !DIExpression(DW_OP_entry_value, 4, DW_OP_constu, 0, DW_OP_stack_value) -!1 = !DIExpression(DW_OP_constu, 0, DW_OP_entry_value, 1, DW_OP_constu, 0) -!2 = !DIExpression(DW_OP_entry_value, 100, DW_OP_constu, 0) +; CHECK-NEXT: !DIExpression +; CHECK: invalid expression +; CHECK-NEXT: !DIExpression +; CHECK: invalid expression +; CHECK-NEXT: !DIExpression +!0 = !DIExpression(DW_OP_LLVM_entry_value, 4, DW_OP_constu, 0, DW_OP_stack_value) +!1 = !DIExpression(DW_OP_constu, 0, DW_OP_LLVM_entry_value, 1, DW_OP_constu, 0) +!2 = !DIExpression(DW_OP_LLVM_entry_value, 100, DW_OP_constu, 0) diff --git a/llvm/test/Verifier/diexpression-valid-entry-value.ll b/llvm/test/Verifier/diexpression-valid-entry-value.ll --- a/llvm/test/Verifier/diexpression-valid-entry-value.ll +++ b/llvm/test/Verifier/diexpression-valid-entry-value.ll @@ -2,4 +2,4 @@ !named = !{!0} ; CHECK-NOT: invalid expression -!0 = !DIExpression(DW_OP_entry_value, 1) +!0 = !DIExpression(DW_OP_LLVM_entry_value, 1)