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 @@ -2841,6 +2841,13 @@ /// DW_OP_LLVM_arg op as its first operand, or if it contains none. bool isSingleLocationExpression() const; + /// Returns a reference to the elements contained in this expression, skipping + /// past the leading `DW_OP_LLVM_arg, 0` if one is present. + /// Similar to `convertToNonVariadicExpression`, but faster and cheaper - it + /// does not check whether the expression is a single-location expression, and + /// it returns elements rather than creating a new DIExpression. + ArrayRef getSingleLocationExpressionElements() const; + /// Removes all elements from \p Expr that do not apply to an undef debug /// value, which includes every operator that computes the value/location on /// the DWARF stack, including any DW_OP_LLVM_arg elements (making the result @@ -2859,6 +2866,9 @@ /// single debug operand at the start of the expression, then return that /// expression in a non-variadic form by removing DW_OP_LLVM_arg from the /// expression if it is present; otherwise returns std::nullopt. + /// See also `getSingleLocationExpressionElements` above, which skips + /// checking `isSingleLocationExpression` and returns a list of elements + /// rather than a DIExpression. static std::optional convertToNonVariadicExpression(const DIExpression *Expr); 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 @@ -789,7 +789,7 @@ if (Entry->isLocation()) { addVariableAddress(DV, *VariableDie, Entry->getLoc()); } else if (Entry->isInt()) { - auto *Expr = DV.getSingleExpression(); + auto *Expr = DVal->getExpression(); if (Expr && Expr->getNumElements()) { DIELoc *Loc = new (DIEValueAllocator) DIELoc; DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc); @@ -823,7 +823,7 @@ return Entry.isLocation() && !Entry.getLoc().getReg(); })) return VariableDie; - const DIExpression *Expr = DV.getSingleExpression(); + const DIExpression *Expr = DVal->getExpression(); assert(Expr && "Variadic Debug Value must have an Expression."); DIELoc *Loc = new (DIEValueAllocator) DIELoc; DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -154,9 +154,12 @@ assert(!Value.getExpression()->isFragment() && "Fragments not supported."); ValueLoc = std::make_unique(Value); - if (auto *E = ValueLoc->getExpression()) - if (E->getNumElements()) - FrameIndexExprs.push_back({0, E}); + // Use the DbgValueLoc's expression as a FrameIndexExpr iff it is suitable, + // which requires it to be non-variadic. + if (auto E = DIExpression::convertToNonVariadicExpression( + ValueLoc->getExpression())) + if ((*E)->getNumElements()) + FrameIndexExprs.push_back({0, *E}); } /// Initialize from a DBG_VALUE instruction. 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 @@ -234,7 +234,15 @@ /// Get .debug_loc entry for the instruction range starting at MI. static DbgValueLoc getDebugLocValue(const MachineInstr *MI) { const DIExpression *Expr = MI->getDebugExpression(); - const bool IsVariadic = MI->isDebugValueList(); + const bool IsVariadic = !Expr->isSingleLocationExpression(); + // If we have a variadic debug value instruction that is equivalent to a + // non-variadic instruction, then convert it to non-variadic form here. + if (!IsVariadic && !MI->isNonListDebugValue()) { + assert(MI->getNumDebugOperands() == 1 && + "Mismatched DIExpression and debug operands for debug instruction."); + Expr = DIExpression::get(Expr->getContext(), + Expr->getSingleLocationExpressionElements()); + } assert(MI->getNumOperands() >= 3); SmallVector DbgValueLocEntries; for (const MachineOperand &Op : MI->debug_operands()) { @@ -266,9 +274,12 @@ "Wrong inlined-at"); ValueLoc = std::make_unique(getDebugLocValue(DbgValue)); - if (auto *E = DbgValue->getDebugExpression()) - if (E->getNumElements()) - FrameIndexExprs.push_back({0, E}); + // Use the debug value's expression as a FrameIndexExpr iff it is suitable, + // which requires it to be non-variadic. + if (auto E = DIExpression::convertToNonVariadicExpression( + DbgValue->getDebugExpression())) + if ((*E)->getNumElements()) + FrameIndexExprs.push_back({0, *E}); } ArrayRef DbgVariable::getFrameIndexExprs() const { 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 @@ -1345,13 +1345,17 @@ DEFINE_GETIMPL_STORE_NO_OPS(DIExpression, (Elements)); } bool DIExpression::isEntryValue() const { - return getNumElements() > 0 && getElement(0) == dwarf::DW_OP_LLVM_entry_value; + auto singleLocElts = getSingleLocationExpressionElements(); + return singleLocElts.size() > 0 && + singleLocElts[0] == dwarf::DW_OP_LLVM_entry_value; } bool DIExpression::startsWithDeref() const { - return getNumElements() > 0 && getElement(0) == dwarf::DW_OP_deref; + auto singleLocElts = getSingleLocationExpressionElements(); + return singleLocElts.size() > 0 && singleLocElts[0] == dwarf::DW_OP_deref; } bool DIExpression::isDeref() const { - return getNumElements() == 1 && startsWithDeref(); + auto singleLocElts = getSingleLocationExpressionElements(); + return singleLocElts.size() == 1 && singleLocElts[0] == dwarf::DW_OP_deref; } DIAssignID *DIAssignID::getImpl(LLVMContext &Context, StorageType Storage, @@ -1528,14 +1532,24 @@ auto ExprOpBegin = expr_ops().begin(); auto ExprOpEnd = expr_ops().end(); - if (ExprOpBegin->getOp() == dwarf::DW_OP_LLVM_arg) + if (ExprOpBegin->getOp() == dwarf::DW_OP_LLVM_arg) { + if (ExprOpBegin->getArg(0) != 0) + return false; ++ExprOpBegin; + } return !std::any_of(ExprOpBegin, ExprOpEnd, [](auto Op) { return Op.getOp() == dwarf::DW_OP_LLVM_arg; }); } +ArrayRef DIExpression::getSingleLocationExpressionElements() const { + if (getNumElements() < 2 || Elements[0] != dwarf::DW_OP_LLVM_arg || + Elements[1] != 0) + return Elements; + return getElements().drop_front(2); +} + const DIExpression * DIExpression::convertToUndefExpression(const DIExpression *Expr) { SmallVector UndefOps; @@ -1561,6 +1575,9 @@ std::optional DIExpression::convertToNonVariadicExpression(const DIExpression *Expr) { + if (!Expr) + return std::nullopt; + // Check for `isValid` covered by `isSingleLocationExpression`. if (!Expr->isSingleLocationExpression()) return std::nullopt; @@ -1575,9 +1592,8 @@ if (*ElementsBegin != dwarf::DW_OP_LLVM_arg) return Expr; - SmallVector NonVariadicOps( - make_range(ElementsBegin + 2, Expr->elements_end())); - return DIExpression::get(Expr->getContext(), NonVariadicOps); + return DIExpression::get(Expr->getContext(), + Expr->getElements().drop_front(2)); } void DIExpression::canonicalizeExpressionOps(SmallVectorImpl &Ops, @@ -1648,23 +1664,25 @@ } bool DIExpression::extractIfOffset(int64_t &Offset) const { - if (getNumElements() == 0) { + auto SingleLocElts = getSingleLocationExpressionElements(); + if (SingleLocElts.size() == 0) { Offset = 0; return true; } - if (getNumElements() == 2 && Elements[0] == dwarf::DW_OP_plus_uconst) { - Offset = Elements[1]; + if (SingleLocElts.size() == 2 && + SingleLocElts[0] == dwarf::DW_OP_plus_uconst) { + Offset = SingleLocElts[1]; return true; } - if (getNumElements() == 3 && Elements[0] == dwarf::DW_OP_constu) { - if (Elements[2] == dwarf::DW_OP_plus) { - Offset = Elements[1]; + if (SingleLocElts.size() == 3 && SingleLocElts[0] == dwarf::DW_OP_constu) { + if (SingleLocElts[2] == dwarf::DW_OP_plus) { + Offset = SingleLocElts[1]; return true; } - if (Elements[2] == dwarf::DW_OP_minus) { - Offset = -Elements[1]; + if (SingleLocElts[2] == dwarf::DW_OP_minus) { + Offset = -SingleLocElts[1]; return true; } } @@ -1687,18 +1705,19 @@ unsigned &AddrClass) { // FIXME: This seems fragile. Nothing that verifies that these elements // actually map to ops and not operands. + auto SingleLocElts = Expr->getSingleLocationExpressionElements(); const unsigned PatternSize = 4; - if (Expr->Elements.size() >= PatternSize && - Expr->Elements[PatternSize - 4] == dwarf::DW_OP_constu && - Expr->Elements[PatternSize - 2] == dwarf::DW_OP_swap && - Expr->Elements[PatternSize - 1] == dwarf::DW_OP_xderef) { - AddrClass = Expr->Elements[PatternSize - 3]; + if (SingleLocElts.size() >= PatternSize && + SingleLocElts[PatternSize - 4] == dwarf::DW_OP_constu && + SingleLocElts[PatternSize - 2] == dwarf::DW_OP_swap && + SingleLocElts[PatternSize - 1] == dwarf::DW_OP_xderef) { + AddrClass = SingleLocElts[PatternSize - 3]; - if (Expr->Elements.size() == PatternSize) + if (SingleLocElts.size() == PatternSize) return nullptr; - return DIExpression::get(Expr->getContext(), - ArrayRef(&*Expr->Elements.begin(), - Expr->Elements.size() - PatternSize)); + return DIExpression::get( + Expr->getContext(), + ArrayRef(&*SingleLocElts.begin(), SingleLocElts.size() - PatternSize)); } return Expr; } diff --git a/llvm/test/DebugInfo/X86/debug-value-list-entry-value.mir b/llvm/test/DebugInfo/X86/debug-value-list-entry-value.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/debug-value-list-entry-value.mir @@ -0,0 +1,82 @@ +# RUN: llc %s --start-after=livedebugvalues --filetype=obj -o - \ +# RUN: | llvm-dwarfdump - --name=test-var -o - | FileCheck %s + +# Test that when an entry value expression appears in a DBG_VALUE_LIST, we are +# able to produce a valid entry value location in DWARF. + +# CHECK: DW_OP_entry_value(DW_OP_reg14 R14), DW_OP_plus_uconst 0x10, DW_OP_plus_uconst 0x10, DW_OP_deref + +--- | + source_filename = "test.ll" + target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-apple-macosx12.1.0" + + declare void @llvm.dbg.value(metadata, metadata, metadata) + + define swifttailcc void @blah(ptr swiftasync %0) !dbg !15 { + %use = getelementptr i8, ptr %0, i64 9 + call void @llvm.dbg.value(metadata ptr %0, metadata !18, metadata !DIExpression(DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_plus_uconst, 16, DW_OP_deref, DW_OP_deref)), !dbg !22 + %use1 = load i32, ptr null, align 4, !dbg !27 + %use2 = sext i32 %use1 to i64 + %use3 = getelementptr i8, ptr null, i64 %use2 + store ptr %use3, ptr %0, align 8 + ret void + } + + !llvm.module.flags = !{!0} + !llvm.dbg.cu = !{!1} + + !0 = !{i32 2, !"Debug Info Version", i32 3} + !1 = distinct !DICompileUnit(language: DW_LANG_Swift, file: !2, producer: "blah", isOptimized: true, flags: "blah", runtimeVersion: 5, emissionKind: FullDebug, globals: !3, imports: !9, sysroot: "blah", sdk: "blah") + !2 = !DIFile(filename: "blah", directory: "blah") + !3 = !{!4, !10} + !4 = !DIGlobalVariableExpression(var: !5, expr: !DIExpression()) + !5 = distinct !DIGlobalVariable(name: "blah", linkageName: "blah", scope: !6, file: !2, line: 49, type: !7, isLocal: true, isDefinition: true) + !6 = !DIModule(scope: null, name: "blah", includePath: "blah") + !7 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !8) + !8 = !DICompositeType(tag: DW_TAG_structure_type, name: "blah", scope: !6, file: !2, size: 64, elements: !9, runtimeLang: DW_LANG_Swift, identifier: "blah") + !9 = !{} + !10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression()) + !11 = distinct !DIGlobalVariable(name: "blah", linkageName: "blah", scope: !6, file: !2, line: 44, type: !12, isLocal: false, isDefinition: true) + !12 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !13) + !13 = !DICompositeType(tag: DW_TAG_structure_type, name: "blah", scope: !14, file: !2, size: 64, elements: !9, runtimeLang: DW_LANG_Swift, identifier: "blah") + !14 = !DIModule(scope: null, name: "blah", configMacros: "blah", includePath: "blah") + !15 = distinct !DISubprogram(name: "blah", linkageName: "blah", scope: !16, file: !2, line: 115, type: !17, scopeLine: 117, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !1, retainedNodes: !9, thrownTypes: !9) + !16 = !DICompositeType(tag: DW_TAG_structure_type, name: "blah", scope: !6, file: !2, elements: !9, runtimeLang: DW_LANG_Swift, identifier: "blah") + !17 = !DISubroutineType(types: !9) + !18 = !DILocalVariable(name: "test-var", arg: 1, scope: !19, file: !2, line: 95, type: !21, flags: DIFlagArtificial) + !19 = distinct !DISubprogram(name: "blah", linkageName: "blah", scope: !16, file: !2, line: 95, type: !20, scopeLine: 95, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !1, retainedNodes: !9) + !20 = distinct !DISubroutineType(types: !9) + !21 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !16) + !22 = !DILocation(line: 95, column: 9, scope: !19, inlinedAt: !23) + !23 = distinct !DILocation(line: 0, scope: !24, inlinedAt: !25) + !24 = distinct !DISubprogram(name: "blah", linkageName: "blah", scope: !16, file: !2, type: !20, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !1) + !25 = distinct !DILocation(line: 121, column: 36, scope: !26) + !26 = distinct !DILexicalBlock(scope: !15, file: !2, line: 116, column: 7) + !27 = !DILocation(line: 0, scope: !28, inlinedAt: !23) + !28 = !DILexicalBlockFile(scope: !19, file: !2, discriminator: 0) + +... +--- +name: blah +alignment: 16 +tracksRegLiveness: true +debugInstrRef: true +tracksDebugUserValues: true +registers: [] +liveins: + - { reg: '$r14', virtual-reg: '' } +frameInfo: + maxAlignment: 1 +body: | + bb.0 (%ir-block.1): + liveins: $r14 + + DBG_PHI $r14, 1 + DBG_INSTR_REF !18, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_plus_uconst, 16, DW_OP_deref, DW_OP_deref), dbg-instr-ref(1, 0), debug-location !22 + DBG_VALUE_LIST !18, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_entry_value, 1, DW_OP_plus_uconst, 16, DW_OP_plus_uconst, 16, DW_OP_deref, DW_OP_deref), $r14, debug-location !DILocation(line: 0, scope: !19, inlinedAt: !23) + renamable $rax = MOVSX64rm32 $noreg, 1, $noreg, 0, $noreg, debug-location !27 :: (load (s32) from `ptr null`) + MOV64mr killed renamable $r14, 1, $noreg, 0, $noreg, killed renamable $rax :: (store (s64) into %ir.0) + RETI64 8 + +...