diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -4824,6 +4824,9 @@ - ``DW_OP_LLVM_implicit_pointer, DW_OP_LLVM_arg0 N`` can only appear at the beginning of a ``DIExpression``, and it specifies the value of variable represented by first operand at offset N. +- ``DW_OP_LLVM_explicit_pointer, DW_OP_LLVM_arg0`` can only appear at the + beginning of a ``DIExpression``, and it specifies the value of variable + represented by first operand. DWARF specifies three kinds of simple location descriptions: Register, memory, and implicit location descriptions. Note that a location description is 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 @@ -122,6 +122,7 @@ DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata. DW_OP_LLVM_entry_value = 0x1003, ///< Only used in LLVM metadata. DW_OP_LLVM_implicit_pointer = 0x1004, ///< Only used in LLVM metadata. + DW_OP_LLVM_explicit_pointer = 0x1005 ///< Only used in LLVM metadata. }; enum TypeKind : uint8_t { diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def --- a/llvm/include/llvm/BinaryFormat/Dwarf.def +++ b/llvm/include/llvm/BinaryFormat/Dwarf.def @@ -652,6 +652,7 @@ // Extensions for Fission proposal. HANDLE_DW_OP(0xfb, GNU_addr_index, 0, GNU) HANDLE_DW_OP(0xfc, GNU_const_index, 0, GNU) +HANDLE_DW_OP(0xfe, explicit_pointer, 0, LLVM) // The LLVM extension. HANDLE_DW_OP(0x4000, LLVM_arg0, 0, LLVM) HANDLE_DW_OP(0x4001, LLVM_arg1, 0, LLVM) 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 @@ -2473,6 +2473,12 @@ (expr_op_begin()->getOp() == dwarf::DW_OP_LLVM_implicit_pointer); } + /// Return whether this is an Implicit Pointer + bool isExplicitPointer() const { + return getNumElements() > 0 && + (expr_op_begin()->getOp() == dwarf::DW_OP_LLVM_explicit_pointer); + } + /// Return whether this is an implicit location description. bool isImplicit() const; diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h --- a/llvm/include/llvm/IR/IntrinsicInst.h +++ b/llvm/include/llvm/IR/IntrinsicInst.h @@ -131,6 +131,14 @@ return getExpression()->isImplicitPointer(); } + /// Returns true if it has an explicit pointer expression + bool isExplicitPointer() const { + if (!isa(getRawExpression())) + return false; + + return getExpression()->isExplicitPointer(); + } + /// \name Casting methods /// @{ static bool classof(const IntrinsicInst *I) { 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 @@ -153,6 +153,8 @@ return "DW_OP_LLVM_entry_value"; case DW_OP_LLVM_implicit_pointer: return "DW_OP_LLVM_implicit_pointer"; + case DW_OP_LLVM_explicit_pointer: + return "DW_OP_LLVM_explicit_pointer"; } } @@ -166,6 +168,7 @@ .Case("DW_OP_LLVM_tag_offset", DW_OP_LLVM_tag_offset) .Case("DW_OP_LLVM_entry_value", DW_OP_LLVM_entry_value) .Case("DW_OP_LLVM_implicit_pointer", DW_OP_LLVM_implicit_pointer) + .Case("DW_OP_LLVM_explicit_pointer", DW_OP_LLVM_explicit_pointer) .Default(0); } diff --git a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h --- a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h +++ b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h @@ -75,6 +75,12 @@ bool isConstantFP() const { return EntryKind == E_ConstantFP; } bool isConstantInt() const { return EntryKind == E_ConstantInt; } bool isImplicitPtr() const { return EntryKind == E_ImplicitPtr; } + bool isExplicitPtr() const { + const DIExpression* DIExpr = getExpression(); + if(!DIExpr) + return false; + return DIExpr->isExplicitPointer(); + } int64_t getInt() const { return Constant.Int; } const ConstantFP *getConstantFP() const { return Constant.CFP; } const ConstantInt *getConstantInt() const { return Constant.CIP; } 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 @@ -1597,6 +1597,7 @@ bool SingleValueWithClobber = HistSize == 2 && HistoryMapEntries[1].isClobber(); if (!MInsn->getDebugExpression()->isImplicitPointer() && + !MInsn->getDebugExpression()->isExplicitPointer() && (HistSize == 1 || SingleValueWithClobber)) { const auto *End = SingleValueWithClobber ? HistoryMapEntries[1].getInstr() : nullptr; @@ -1623,6 +1624,7 @@ // In case of implicit pointer, implicit target can not initialize // variable so we need to create location list even in case of single value if (!MInsn->getDebugExpression()->isImplicitPointer() && + !MInsn->getDebugExpression()->isExplicitPointer() && isValidSingleLocation) { RegVar->initializeDbgValue(Entries[0].getValues()[0]); continue; @@ -2264,6 +2266,10 @@ auto *DIExpr = Value.getExpression(); DIExpressionCursor ExprCursor(DIExpr); DwarfExpr.addFragmentOffset(DIExpr); + + if (Value.isExplicitPtr()) + DwarfExpr.emitExplicitPointer(std::move(ExprCursor)); + // Regular entry. if (Value.isInt()) { if (BT && (BT->getEncoding() == dwarf::DW_ATE_signed || @@ -2297,7 +2303,9 @@ DwarfExpr.emitImplicitPointer(std::move(ExprCursor), ValOffset); return; } - DwarfExpr.addExpression(std::move(ExprCursor)); + + if (!Value.isExplicitPtr()) + DwarfExpr.addExpression(std::move(ExprCursor)); } void DebugLocEntry::finalize(const AsmPrinter &AP, 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 @@ -340,6 +340,7 @@ void emitLegacySExt(unsigned FromBits); void emitLegacyZExt(unsigned FromBits); void emitImplicitPointer(DIExpressionCursor &&Expr, unsigned ValOffset); + void emitExplicitPointer(DIExpressionCursor &&Expr); }; /// DwarfExpression implementation for .debug_loc entries. 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 @@ -586,3 +586,12 @@ emitSigned(Op->getArg(1)); return; } + +void DwarfExpression::emitExplicitPointer(DIExpressionCursor &&ExprCursor) { + auto Op = ExprCursor.take(); + assert(Op->getOp() == dwarf::DW_OP_LLVM_explicit_pointer && + "not dwarf::DW_OP_LLVM_explicit_pointer"); + + emitOp(dwarf::DW_OP_explicit_pointer); + return; +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -103,6 +103,7 @@ Descriptions[DW_OP_entry_value] = Desc(Op::Dwarf5, Op::SizeLEB); Descriptions[DW_OP_implicit_pointer] = Desc(Op::Dwarf5, Op::SizeRefAddr, Op::SignedSizeLEB); + Descriptions[DW_OP_explicit_pointer] = Desc(Op::Dwarf5); return Descriptions; } diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -2153,6 +2153,8 @@ } else if (I->getOp() == dwarf::DW_OP_LLVM_implicit_pointer) { Out << FS << dwarf::OperationEncodingString(I->getArg(0)); Out << FS << I->getArg(1); + } else if (I->getOp() == dwarf::DW_OP_LLVM_explicit_pointer) { + Out << FS << dwarf::OperationEncodingString(I->getArg(0)); } else { for (unsigned A = 0, AE = I->getNumArgs(); A != AE; ++A) Out << FS << I->getArg(A); 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 @@ -846,6 +846,7 @@ case dwarf::DW_OP_LLVM_tag_offset: case dwarf::DW_OP_LLVM_entry_value: case dwarf::DW_OP_regx: + case dwarf::DW_OP_LLVM_explicit_pointer: return 2; default: return 1; @@ -900,8 +901,10 @@ return I->get() == expr_op_begin()->get() && I->getArg(0) == 1 && getNumElements() == 2; } + case dwarf::DW_OP_LLVM_explicit_pointer: case dwarf::DW_OP_LLVM_implicit_pointer: { - // A DW_OP_LLVM_implicit_pointer operator must appear at the beginning + // DW_OP_LLVM_implicit_pointer or DW_OP_LLVM_explicit_pointer operator + // must appear at the beginning return I == expr_op_begin(); } case dwarf::DW_OP_LLVM_convert: diff --git a/llvm/lib/IR/IntrinsicInst.cpp b/llvm/lib/IR/IntrinsicInst.cpp --- a/llvm/lib/IR/IntrinsicInst.cpp +++ b/llvm/lib/IR/IntrinsicInst.cpp @@ -58,7 +58,16 @@ if (AllowNullOp && !Op) return nullptr; - return Op; + if (isImplicitPointer()) + return Op; + + auto *MD = cast(Op)->getMetadata(); + if (auto *V = dyn_cast(MD)) + return V->getValue(); + + // When the value goes to null, it gets replaced by an empty MDNode. + assert(!cast(MD)->getNumOperands() && "Expected an empty MDNode"); + return nullptr; } Optional DbgVariableIntrinsic::getFragmentSizeInBits() const { diff --git a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp --- a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp +++ b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp @@ -417,6 +417,28 @@ ConvertDebugDeclareToDebugValue(DII, Info.OnlyStore, DIB); DII->eraseFromParent(); } + + // If there is not dbg.declare then it can be case of temp being + // promoted, lets record debuginfo + if ((AI->getModule()->getDwarfVersion() >= 5) && !Info.DbgDeclares.size()) { + Value *ValAddr = OnlyStore->getOperand(1); + Value *ReplVal = OnlyStore->getOperand(0); + auto &Ctx = AI->getContext(); + SmallVector DbgUsers; + findDbgUsers(DbgUsers, ValAddr); + for (auto *DII : DbgUsers) { + SmallVector Ops; + Ops.push_back(dwarf::DW_OP_LLVM_explicit_pointer); + Ops.push_back(dwarf::DW_OP_LLVM_arg0); + DIExpression *DIExpr = + DIExpression::get(DII->getExpression()->getContext(), Ops); + DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false); + DIB.insertDbgDerefValueIntrinsic( + MetadataAsValue::get(Ctx, ValueAsMetadata::get(ReplVal)), + DII->getVariable(), DIExpr, DII->getDebugLoc(), DII); + } + } + // Remove the (now dead) store and alloca. Info.OnlyStore->eraseFromParent(); LBI.deleteValue(Info.OnlyStore); diff --git a/llvm/test/DebugInfo/dwarfdump_explicit_pointer.ll b/llvm/test/DebugInfo/dwarfdump_explicit_pointer.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/dwarfdump_explicit_pointer.ll @@ -0,0 +1,95 @@ +; RUN: llc %s -O2 -filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o | FileCheck %s + +; CHECK: : DW_OP_explicit_pointer, DW_OP_lit3 + +; Below is the original test case this IR is generated from +;---------------------------------------------------------- +;__attribute__((optnone)) int source() { +; return 3; +;} +;__attribute__((optnone)) void f(int i) { +;} +;inline void sink(const int& p) { +; f(p); +;} +;int main() { +; sink(source()); +;} +;---------------------------------------------------------- + +; ModuleID = 'explicit_pointer.cc' +source_filename = "explicit_pointer.cc" +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" + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @_Z6sourcev() local_unnamed_addr !dbg !7 { +entry: + ret i32 3, !dbg !11 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @_Z1fi(i32 %i) local_unnamed_addr !dbg !12 { +entry: + %i.addr = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4, !tbaa !17 + call void @llvm.dbg.declare(metadata i32* %i.addr, metadata !16, metadata !DIExpression()), !dbg !21 + ret void, !dbg !22 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +; Function Attrs: norecurse nounwind uwtable +define dso_local i32 @main() local_unnamed_addr !dbg !23 { +entry: + %call = tail call i32 @_Z6sourcev(), !dbg !24 + call void @llvm.dbg.derefval(metadata i32 3, metadata !25, metadata !DIExpression(DW_OP_LLVM_explicit_pointer, DW_OP_LLVM_arg0)), !dbg !32 + tail call void @_Z1fi(i32 3) #3, !dbg !34 + ret i32 0, !dbg !35 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.derefval(metadata, metadata, metadata) + +!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, nameTableKind: None) +!1 = !DIFile(filename: "explicit_pointer.cc", directory: "/dir") +!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: "source", linkageName: "_Z6sourcev", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !DILocation(line: 2, column: 3, scope: !7) +!12 = distinct !DISubprogram(name: "f", linkageName: "_Z1fi", scope: !1, file: !1, line: 4, type: !13, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{null, !10} +!15 = !{!16} +!16 = !DILocalVariable(name: "i", arg: 1, scope: !12, file: !1, line: 4, type: !10) +!17 = !{!18, !18, i64 0} +!18 = !{!"int", !19, i64 0} +!19 = !{!"omnipotent char", !20, i64 0} +!20 = !{!"Simple C++ TBAA"} +!21 = !DILocation(line: 4, column: 37, scope: !12) +!22 = !DILocation(line: 5, column: 1, scope: !12) +!23 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 9, type: !8, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) +!24 = !DILocation(line: 10, column: 8, scope: !23) +!25 = !DILocalVariable(name: "p", arg: 1, scope: !26, file: !1, line: 6, type: !29) +!26 = distinct !DISubprogram(name: "sink", linkageName: "_Z4sinkRKi", scope: !1, file: !1, line: 6, type: !27, scopeLine: 6, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !31) +!27 = !DISubroutineType(types: !28) +!28 = !{null, !29} +!29 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !30, size: 64) +!30 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !10) +!31 = !{!25} +!32 = !DILocation(line: 0, scope: !26, inlinedAt: !33) +!33 = distinct !DILocation(line: 10, column: 3, scope: !23) +!34 = !DILocation(line: 7, column: 3, scope: !26, inlinedAt: !33) +!35 = !DILocation(line: 11, column: 1, scope: !23) diff --git a/llvm/test/DebugInfo/explicit_pointer.cc b/llvm/test/DebugInfo/explicit_pointer.cc new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/explicit_pointer.cc @@ -0,0 +1,16 @@ +// RUN: clang %s -O2 -gdwarf-5 -S -emit-llvm -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll + +// CHECK: call void @llvm.dbg.derefval(metadata i32 3, {{.+}} !DIExpression(DW_OP_LLVM_explicit_pointer, DW_OP_LLVM_arg0) + +__attribute__((optnone)) int source() { + return 3; +} +__attribute__((optnone)) void f(int i) { +} +inline void sink(const int& p) { + f(p); +} +int main() { + sink(source()); +}