diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h --- a/llvm/include/llvm/IR/DIBuilder.h +++ b/llvm/include/llvm/IR/DIBuilder.h @@ -23,6 +23,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/TrackingMDRef.h" #include "llvm/Support/Casting.h" #include @@ -46,6 +47,7 @@ Function *DeclareFn; ///< llvm.dbg.declare Function *ValueFn; ///< llvm.dbg.value Function *LabelFn; ///< llvm.dbg.label + Function *AddrFn; ///< llvm.dbg.addr SmallVector AllEnumTypes; /// Track the RetainTypes, since they can be updated later on. @@ -86,11 +88,34 @@ Instruction *insertLabel(DILabel *LabelInfo, const DILocation *DL, BasicBlock *InsertBB, Instruction *InsertBefore); + /// Internal helper with common code used by insertDbg{Value,Addr}Intrinsic. + Instruction *insertDbgIntrinsic(llvm::Function *Intrinsic, llvm::Value *Val, + DILocalVariable *VarInfo, + DIExpression *Expr, const DILocation *DL, + BasicBlock *InsertBB, + Instruction *InsertBefore); + /// Internal helper for insertDbgValueIntrinsic. Instruction * insertDbgValueIntrinsic(llvm::Value *Val, DILocalVariable *VarInfo, DIExpression *Expr, const DILocation *DL, - BasicBlock *InsertBB, Instruction *InsertBefore); + BasicBlock *InsertBB, Instruction *InsertBefore) { + if (!ValueFn) + ValueFn = Intrinsic::getDeclaration(&M, Intrinsic::dbg_value); + return insertDbgIntrinsic(ValueFn, Val, VarInfo, Expr, DL, InsertBB, + InsertBefore); + } + + /// Internal helper for insertDbgAddrIntrinsic. + Instruction * + insertDbgAddrIntrinsic(llvm::Value *Val, DILocalVariable *VarInfo, + DIExpression *Expr, const DILocation *DL, + BasicBlock *InsertBB, Instruction *InsertBefore) { + if (!AddrFn) + AddrFn = Intrinsic::getDeclaration(&M, Intrinsic::dbg_addr); + return insertDbgIntrinsic(AddrFn, Val, VarInfo, Expr, DL, InsertBB, + InsertBefore); + } public: /// Construct a builder for a module. @@ -929,6 +954,30 @@ const DILocation *DL, Instruction *InsertBefore); + /// Insert a new llvm.dbg.addr intrinsic call. + /// \param Addr llvm::Value of the address + /// \param VarInfo Variable's debug info descriptor. + /// \param Expr A complex location expression. + /// \param DL Debug info location. + /// \param InsertAtEnd Location for the new intrinsic. + Instruction *insertDbgAddrIntrinsic(llvm::Value *Addr, + DILocalVariable *VarInfo, + DIExpression *Expr, + const DILocation *DL, + BasicBlock *InsertAtEnd); + + /// Insert a new llvm.dbg.addr intrinsic call. + /// \param Addr llvm::Value of the address. + /// \param VarInfo Variable's debug info descriptor. + /// \param Expr A complex location expression. + /// \param DL Debug info location. + /// \param InsertBefore Location for the new intrinsic. + Instruction *insertDbgAddrIntrinsic(llvm::Value *Addr, + DILocalVariable *VarInfo, + DIExpression *Expr, + const DILocation *DL, + Instruction *InsertBefore); + /// Replace the vtable holder in the given type. /// /// If this creates a self reference, it may orphan some unresolved cycles diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -33,7 +33,7 @@ DIBuilder::DIBuilder(Module &m, bool AllowUnresolvedNodes, DICompileUnit *CU) : M(m), VMContext(M.getContext()), CUNode(CU), DeclareFn(nullptr), - ValueFn(nullptr), LabelFn(nullptr), + ValueFn(nullptr), LabelFn(nullptr), AddrFn(nullptr), AllowUnresolvedNodes(AllowUnresolvedNodes) { if (CUNode) { if (const auto &ETs = CUNode->getEnumTypes()) @@ -974,6 +974,24 @@ return insertDbgValueIntrinsic(V, VarInfo, Expr, DL, InsertAtEnd, nullptr); } +Instruction *DIBuilder::insertDbgAddrIntrinsic(Value *V, + DILocalVariable *VarInfo, + DIExpression *Expr, + const DILocation *DL, + Instruction *InsertBefore) { + return insertDbgAddrIntrinsic( + V, VarInfo, Expr, DL, InsertBefore ? InsertBefore->getParent() : nullptr, + InsertBefore); +} + +Instruction *DIBuilder::insertDbgAddrIntrinsic(Value *V, + DILocalVariable *VarInfo, + DIExpression *Expr, + const DILocation *DL, + BasicBlock *InsertAtEnd) { + return insertDbgAddrIntrinsic(V, VarInfo, Expr, DL, InsertAtEnd, nullptr); +} + /// Initialize IRBuilder for inserting dbg.declare and dbg.value intrinsics. /// This abstracts over the various ways to specify an insert position. static void initIRBuilder(IRBuilder<> &Builder, const DILocation *DL, @@ -1018,17 +1036,20 @@ return B.CreateCall(DeclareFn, Args); } -Instruction *DIBuilder::insertDbgValueIntrinsic( - Value *V, DILocalVariable *VarInfo, DIExpression *Expr, - const DILocation *DL, BasicBlock *InsertBB, Instruction *InsertBefore) { - assert(V && "no value passed to dbg.value"); - assert(VarInfo && "empty or invalid DILocalVariable* passed to dbg.value"); +Instruction *DIBuilder::insertDbgIntrinsic(llvm::Function *IntrinsicFn, + Value *V, DILocalVariable *VarInfo, + DIExpression *Expr, + const DILocation *DL, + BasicBlock *InsertBB, + Instruction *InsertBefore) { + assert(IntrinsicFn && "must pass a non-null intrinsic function"); + assert(V && "must pass a value to a dbg intrinsic"); + assert(VarInfo && + "empty or invalid DILocalVariable* passed to debug intrinsic"); assert(DL && "Expected debug loc"); assert(DL->getScope()->getSubprogram() == VarInfo->getScope()->getSubprogram() && "Expected matching subprograms"); - if (!ValueFn) - ValueFn = Intrinsic::getDeclaration(&M, Intrinsic::dbg_value); trackIfUnresolved(VarInfo); trackIfUnresolved(Expr); @@ -1038,7 +1059,7 @@ IRBuilder<> B(DL->getContext()); initIRBuilder(B, DL, InsertBB, InsertBefore); - return B.CreateCall(ValueFn, Args); + return B.CreateCall(IntrinsicFn, Args); } Instruction *DIBuilder::insertLabel(DILabel *LabelInfo, const DILocation *DL, diff --git a/llvm/test/DebugInfo/X86/dbg-addr.ll b/llvm/test/DebugInfo/X86/dbg-addr.ll --- a/llvm/test/DebugInfo/X86/dbg-addr.ll +++ b/llvm/test/DebugInfo/X86/dbg-addr.ll @@ -1,11 +1,11 @@ ;; Run twice -- once with DBG_VALUEs, once with instruction referencing. ; RUN: llc %s -o %t.s -experimental-debug-variable-locations=false ; RUN: llvm-mc -triple x86_64--linux %t.s -filetype=obj -o %t.o -; RUN: FileCheck < %t.s %s +; RUN: FileCheck -input-file=%t.s %s ; RUN: llvm-dwarfdump %t.o | FileCheck %s --check-prefix=DWARF ; RUN: llc %s -o %t.s -experimental-debug-variable-locations=true ; RUN: llvm-mc -triple x86_64--linux %t.s -filetype=obj -o %t.o -; RUN: FileCheck < %t.s %s +; RUN: FileCheck -input-file=%t.s %s ; RUN: llvm-dwarfdump %t.o | FileCheck %s --check-prefix=DWARF @@ -20,6 +20,19 @@ ; DWARF-NEXT: DW_AT_location (DW_OP_fbreg +0) ; DWARF-NEXT: DW_AT_name ("o") +; Make sure that in the second case, we properly get a validity range in the +; dwarf for the value. This ensures that we can use this technique to invalidate +; variables. + +; CHECK-LABEL: test_dbg_addr_and_dbg_val_undef +; CHECK: #DEBUG_VALUE: test_dbg_addr_and_dbg_val_undef:second_o <- [$rsp+0] +; CHECK: #DEBUG_VALUE: test_dbg_addr_and_dbg_val_undef:second_o <- undef +; CHECK-NOT: #DEBUG_VALUE: + +; DWARF: DW_TAG_variable +; DWARF-NEXT: DW_AT_location (0x{{[0-9a-z][0-9a-z]*}}: +; DWARF-NEXT: [0x{{[0-9a-z][0-9a-z]*}}, 0x{{[0-9a-z][0-9a-z]*}}): DW_OP_breg7 RSP+0) +; DWARF-NEXT: DW_AT_name ("second_o") ; ModuleID = 't.c' source_filename = "t.c" @@ -37,8 +50,18 @@ ret void, !dbg !18 } +define void @test_dbg_addr_and_dbg_val_undef() #0 !dbg !117 { +entry: + %o = alloca %struct.Foo, align 4 + call void @llvm.dbg.addr(metadata %struct.Foo* %o, metadata !1110, metadata !1115), !dbg !1116 + call void @escape_foo(%struct.Foo* %o), !dbg !1117 + call void @llvm.dbg.value(metadata %struct.Foo* undef, metadata !1110, metadata !1115), !dbg !1116 + ret void, !dbg !1118 +} + ; Function Attrs: nounwind readnone speculatable declare void @llvm.dbg.addr(metadata, metadata, metadata) #1 +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 declare void @escape_foo(%struct.Foo*) @@ -68,3 +91,16 @@ !16 = !DILocation(line: 4, column: 14, scope: !7) !17 = !DILocation(line: 5, column: 3, scope: !7) !18 = !DILocation(line: 6, column: 1, scope: !7) + +!117 = distinct !DISubprogram(name: "test_dbg_addr_and_dbg_val_undef", scope: !1, file: !1, line: 3, type: !118, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2) +!118 = !DISubroutineType(types: !119) +!119 = !{null} +!1110 = !DILocalVariable(name: "second_o", scope: !117, file: !1, line: 4, type: !1111) +!1111 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", file: !1, line: 1, size: 32, elements: !1112) +!1112 = !{!1113} +!1113 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !1111, file: !1, line: 1, baseType: !1114, size: 32) +!1114 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!1115 = !DIExpression() +!1116 = !DILocation(line: 4, column: 14, scope: !117) +!1117 = !DILocation(line: 5, column: 3, scope: !117) +!1118 = !DILocation(line: 6, column: 1, scope: !117) diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp --- a/llvm/unittests/IR/DebugInfoTest.cpp +++ b/llvm/unittests/IR/DebugInfoTest.cpp @@ -11,8 +11,10 @@ #include "llvm/AsmParser/Parser.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/IRBuilder.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/SourceMgr.h" @@ -262,4 +264,69 @@ EXPECT_FALSE(E2); } +TEST(DIBuilder, createDbgAddr) { + LLVMContext C; + std::unique_ptr M = parseIR(C, R"( + define void @f() !dbg !6 { + %a = alloca i16, align 8 + ;; It is important that we put the debug marker on the return. + ;; We take advantage of that to conjure up a debug loc without + ;; having to synthesize one programatically. + ret void, !dbg !11 + } + declare void @llvm.dbg.value(metadata, metadata, metadata) #0 + attributes #0 = { nounwind readnone speculatable willreturn } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!5} + + !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) + !1 = !DIFile(filename: "t.ll", directory: "/") + !2 = !{} + !5 = !{i32 2, !"Debug Info Version", i32 3} + !6 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8) + !7 = !DISubroutineType(types: !2) + !8 = !{!9} + !9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10) + !10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned) + !11 = !DILocation(line: 1, column: 1, scope: !6) +)"); + auto *F = M->getFunction("f"); + auto *EntryBlock = &F->getEntryBlock(); + + auto *CU = + cast(M->getNamedMetadata("llvm.dbg.cu")->getOperand(0)); + auto *Alloca = &*EntryBlock->begin(); + auto *Ret = EntryBlock->getTerminator(); + + auto *SP = cast(F->getMetadata(LLVMContext::MD_dbg)); + auto *File = SP->getFile(); + std::string Name = "myName"; + const auto *Loc = Ret->getDebugLoc().get(); + + IRBuilder<> Builder(EntryBlock); + DIBuilder DIB(*M, true, CU); + DIType *DT = DIB.createBasicType("ty16", 16, dwarf::DW_ATE_unsigned); + + DILocalVariable *LocalVar = + DIB.createAutoVariable(SP, Name, File, 5 /*line*/, DT, + /*AlwaysPreserve=*/true); + + auto *Inst = DIB.insertDbgAddrIntrinsic(Alloca, LocalVar, + DIB.createExpression(), Loc, Ret); + + DIB.finalize(); + + EXPECT_EQ(Inst->getDebugLoc().get(), Loc); + + auto *MD0 = cast(Inst->getOperand(0))->getMetadata(); + auto *MD0Local = cast(MD0); + EXPECT_EQ(MD0Local->getValue(), Alloca); + auto *MD1 = cast(Inst->getOperand(1))->getMetadata(); + EXPECT_EQ(MD1->getMetadataID(), Metadata::MetadataKind::DILocalVariableKind); + auto *MD2 = cast(Inst->getOperand(2))->getMetadata(); + auto *MDExp = cast(MD2); + EXPECT_EQ(MDExp->getNumElements(), 0u); +} + } // end namespace