Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -4825,6 +4825,8 @@ beginning of a ``DIExpression``, and it specifies the value of variable represented by the ``DILocalVariable`` referred to by the instructions value/address operand at offset N. +- ``DW_OP_LLVM_explicit_pointer`` can only appear at the beginning of a + ``DIExpression``, and it specifies the dereferenced value. DWARF specifies three kinds of simple location descriptions: Register, memory, and implicit location descriptions. Note that a location description is Index: llvm/include/llvm/BinaryFormat/Dwarf.def =================================================================== --- llvm/include/llvm/BinaryFormat/Dwarf.def +++ llvm/include/llvm/BinaryFormat/Dwarf.def @@ -645,6 +645,8 @@ HANDLE_DW_OP(0xa8, convert, 5, DWARF) HANDLE_DW_OP(0xa9, reinterpret, 5, DWARF) // Vendor extensions: +// Extensions for LLVM explicit_pointer +HANDLE_DW_OP(0xd0, LLVM_explicit_pointer, 5, LLVM) // Extensions for GNU-style thread-local storage. HANDLE_DW_OP(0xe0, GNU_push_tls_address, 0, GNU) // The GNU entry value extension. Index: llvm/include/llvm/IR/DebugInfoMetadata.h =================================================================== --- llvm/include/llvm/IR/DebugInfoMetadata.h +++ 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; Index: llvm/include/llvm/IR/IntrinsicInst.h =================================================================== --- llvm/include/llvm/IR/IntrinsicInst.h +++ 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) { Index: llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h +++ llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h @@ -75,6 +75,10 @@ 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(); + return DIExpr ? DIExpr->isExplicitPointer() : false; + } int64_t getInt() const { return Constant.Int; } const ConstantFP *getConstantFP() const { return Constant.CFP; } const ConstantInt *getConstantInt() const { return Constant.CIP; } Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1601,6 +1601,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; @@ -1627,6 +1628,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; @@ -2268,6 +2270,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 || @@ -2301,7 +2307,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, Index: llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h +++ 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. Index: llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ 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_LLVM_explicit_pointer); + return; +} Index: llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ 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_LLVM_explicit_pointer] = Desc(Op::Dwarf5); return Descriptions; } Index: llvm/lib/IR/AsmWriter.cpp =================================================================== --- llvm/lib/IR/AsmWriter.cpp +++ 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); Index: llvm/lib/IR/DebugInfoMetadata.cpp =================================================================== --- llvm/lib/IR/DebugInfoMetadata.cpp +++ 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,6 +901,10 @@ return I->get() == expr_op_begin()->get() && I->getArg(0) == 1 && getNumElements() == 2; } + case dwarf::DW_OP_LLVM_explicit_pointer: { + // A DW_OP_LLVM_explicit_pointer operator must appear at the beginning + return I == expr_op_begin(); + } case dwarf::DW_OP_LLVM_implicit_pointer: { // A DW_OP_LLVM_implicit_pointer operator must appear at the beginning // and it has two arguments, DW_OP_LLVM_arg0 and offset N. Index: llvm/lib/IR/IntrinsicInst.cpp =================================================================== --- llvm/lib/IR/IntrinsicInst.cpp +++ 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 { Index: llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp =================================================================== --- llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp +++ llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp @@ -417,6 +417,30 @@ 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) { + DIExpression *DIExpr = DII->getExpression(); + if (DIExpr->getNumElements() == 0) { + SmallVector Ops; + Ops.push_back(dwarf::DW_OP_LLVM_explicit_pointer); + Ops.push_back(dwarf::DW_OP_LLVM_arg0); + DIExpression *DIExprNew = DIExpression::get(DIExpr->getContext(), Ops); + DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false); + DIB.insertDbgDerefValueIntrinsic( + MetadataAsValue::get(Ctx, ValueAsMetadata::get(ReplVal)), + DII->getVariable(), DIExprNew, DII->getDebugLoc(), DII); + } + } + } + // Remove the (now dead) store and alloca. Info.OnlyStore->eraseFromParent(); LBI.deleteValue(Info.OnlyStore); Index: llvm/test/DebugInfo/dwarfdump_explicit_pointer.ll =================================================================== --- /dev/null +++ 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_LLVM_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) Index: llvm/test/DebugInfo/explicit_pointer.cc =================================================================== --- /dev/null +++ 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()); +}