[debug-info] Add support for llvm.dbg.addr in DIBuilder.

I based this off of the API already create for llvm.dbg.value since both
intrinsics have the same arguments at the API level.

I added some tests exercising the API a little as well as an additional small
test that shows how one can use llvm.dbg.addr to limit the PC range where an
address value is available in the debugger. This is done by calling
llvm.dbg.value with undef and the same metadata info as one used to create the
llvm.dbg.addr.

rdar://83957028

Reviewed By: aprantl

Differential Revision: https://reviews.llvm.org/D117442
This commit is contained in:
Michael Gottesman 2022-01-16 12:44:52 -08:00
parent ff0b634d97
commit 7ed95d1577
4 changed files with 185 additions and 12 deletions

View File

@ -23,6 +23,7 @@
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfo.h"
#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/TrackingMDRef.h" #include "llvm/IR/TrackingMDRef.h"
#include "llvm/Support/Casting.h" #include "llvm/Support/Casting.h"
#include <algorithm> #include <algorithm>
@ -46,6 +47,7 @@ namespace llvm {
Function *DeclareFn; ///< llvm.dbg.declare Function *DeclareFn; ///< llvm.dbg.declare
Function *ValueFn; ///< llvm.dbg.value Function *ValueFn; ///< llvm.dbg.value
Function *LabelFn; ///< llvm.dbg.label Function *LabelFn; ///< llvm.dbg.label
Function *AddrFn; ///< llvm.dbg.addr
SmallVector<Metadata *, 4> AllEnumTypes; SmallVector<Metadata *, 4> AllEnumTypes;
/// Track the RetainTypes, since they can be updated later on. /// Track the RetainTypes, since they can be updated later on.
@ -86,11 +88,34 @@ namespace llvm {
Instruction *insertLabel(DILabel *LabelInfo, const DILocation *DL, Instruction *insertLabel(DILabel *LabelInfo, const DILocation *DL,
BasicBlock *InsertBB, Instruction *InsertBefore); 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. /// Internal helper for insertDbgValueIntrinsic.
Instruction * Instruction *
insertDbgValueIntrinsic(llvm::Value *Val, DILocalVariable *VarInfo, insertDbgValueIntrinsic(llvm::Value *Val, DILocalVariable *VarInfo,
DIExpression *Expr, const DILocation *DL, 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: public:
/// Construct a builder for a module. /// Construct a builder for a module.
@ -929,6 +954,30 @@ namespace llvm {
const DILocation *DL, const DILocation *DL,
Instruction *InsertBefore); 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. /// Replace the vtable holder in the given type.
/// ///
/// If this creates a self reference, it may orphan some unresolved cycles /// If this creates a self reference, it may orphan some unresolved cycles

View File

@ -33,7 +33,7 @@ static cl::opt<bool>
DIBuilder::DIBuilder(Module &m, bool AllowUnresolvedNodes, DICompileUnit *CU) DIBuilder::DIBuilder(Module &m, bool AllowUnresolvedNodes, DICompileUnit *CU)
: M(m), VMContext(M.getContext()), CUNode(CU), DeclareFn(nullptr), : M(m), VMContext(M.getContext()), CUNode(CU), DeclareFn(nullptr),
ValueFn(nullptr), LabelFn(nullptr), ValueFn(nullptr), LabelFn(nullptr), AddrFn(nullptr),
AllowUnresolvedNodes(AllowUnresolvedNodes) { AllowUnresolvedNodes(AllowUnresolvedNodes) {
if (CUNode) { if (CUNode) {
if (const auto &ETs = CUNode->getEnumTypes()) if (const auto &ETs = CUNode->getEnumTypes())
@ -974,6 +974,24 @@ Instruction *DIBuilder::insertDbgValueIntrinsic(Value *V,
return insertDbgValueIntrinsic(V, VarInfo, Expr, DL, InsertAtEnd, nullptr); 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. /// Initialize IRBuilder for inserting dbg.declare and dbg.value intrinsics.
/// This abstracts over the various ways to specify an insert position. /// This abstracts over the various ways to specify an insert position.
static void initIRBuilder(IRBuilder<> &Builder, const DILocation *DL, static void initIRBuilder(IRBuilder<> &Builder, const DILocation *DL,
@ -1018,17 +1036,20 @@ Instruction *DIBuilder::insertDeclare(Value *Storage, DILocalVariable *VarInfo,
return B.CreateCall(DeclareFn, Args); return B.CreateCall(DeclareFn, Args);
} }
Instruction *DIBuilder::insertDbgValueIntrinsic( Instruction *DIBuilder::insertDbgIntrinsic(llvm::Function *IntrinsicFn,
Value *V, DILocalVariable *VarInfo, DIExpression *Expr, Value *V, DILocalVariable *VarInfo,
const DILocation *DL, BasicBlock *InsertBB, Instruction *InsertBefore) { DIExpression *Expr,
assert(V && "no value passed to dbg.value"); const DILocation *DL,
assert(VarInfo && "empty or invalid DILocalVariable* passed to dbg.value"); 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 && "Expected debug loc");
assert(DL->getScope()->getSubprogram() == assert(DL->getScope()->getSubprogram() ==
VarInfo->getScope()->getSubprogram() && VarInfo->getScope()->getSubprogram() &&
"Expected matching subprograms"); "Expected matching subprograms");
if (!ValueFn)
ValueFn = Intrinsic::getDeclaration(&M, Intrinsic::dbg_value);
trackIfUnresolved(VarInfo); trackIfUnresolved(VarInfo);
trackIfUnresolved(Expr); trackIfUnresolved(Expr);
@ -1038,7 +1059,7 @@ Instruction *DIBuilder::insertDbgValueIntrinsic(
IRBuilder<> B(DL->getContext()); IRBuilder<> B(DL->getContext());
initIRBuilder(B, DL, InsertBB, InsertBefore); initIRBuilder(B, DL, InsertBB, InsertBefore);
return B.CreateCall(ValueFn, Args); return B.CreateCall(IntrinsicFn, Args);
} }
Instruction *DIBuilder::insertLabel(DILabel *LabelInfo, const DILocation *DL, Instruction *DIBuilder::insertLabel(DILabel *LabelInfo, const DILocation *DL,

View File

@ -1,11 +1,11 @@
;; Run twice -- once with DBG_VALUEs, once with instruction referencing. ;; Run twice -- once with DBG_VALUEs, once with instruction referencing.
; RUN: llc %s -o %t.s -experimental-debug-variable-locations=false ; 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: 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: llvm-dwarfdump %t.o | FileCheck %s --check-prefix=DWARF
; RUN: llc %s -o %t.s -experimental-debug-variable-locations=true ; 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: 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: 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_location (DW_OP_fbreg +0)
; DWARF-NEXT: DW_AT_name ("o") ; 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' ; ModuleID = 't.c'
source_filename = "t.c" source_filename = "t.c"
@ -37,8 +50,18 @@ entry:
ret void, !dbg !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 ; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.addr(metadata, metadata, metadata) #1 declare void @llvm.dbg.addr(metadata, metadata, metadata) #1
declare void @llvm.dbg.value(metadata, metadata, metadata) #1
declare void @escape_foo(%struct.Foo*) declare void @escape_foo(%struct.Foo*)
@ -68,3 +91,16 @@ attributes #1 = { nounwind readnone speculatable }
!16 = !DILocation(line: 4, column: 14, scope: !7) !16 = !DILocation(line: 4, column: 14, scope: !7)
!17 = !DILocation(line: 5, column: 3, scope: !7) !17 = !DILocation(line: 5, column: 3, scope: !7)
!18 = !DILocation(line: 6, column: 1, 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)

View File

@ -11,8 +11,10 @@
#include "llvm/AsmParser/Parser.h" #include "llvm/AsmParser/Parser.h"
#include "llvm/IR/DIBuilder.h" #include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h" #include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h" #include "llvm/IR/Verifier.h"
#include "llvm/Support/SourceMgr.h" #include "llvm/Support/SourceMgr.h"
@ -262,4 +264,69 @@ TEST(DIBuilder, DIEnumerator) {
EXPECT_FALSE(E2); EXPECT_FALSE(E2);
} }
TEST(DIBuilder, createDbgAddr) {
LLVMContext C;
std::unique_ptr<Module> 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<DICompileUnit>(M->getNamedMetadata("llvm.dbg.cu")->getOperand(0));
auto *Alloca = &*EntryBlock->begin();
auto *Ret = EntryBlock->getTerminator();
auto *SP = cast<DISubprogram>(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<MetadataAsValue>(Inst->getOperand(0))->getMetadata();
auto *MD0Local = cast<LocalAsMetadata>(MD0);
EXPECT_EQ(MD0Local->getValue(), Alloca);
auto *MD1 = cast<MetadataAsValue>(Inst->getOperand(1))->getMetadata();
EXPECT_EQ(MD1->getMetadataID(), Metadata::MetadataKind::DILocalVariableKind);
auto *MD2 = cast<MetadataAsValue>(Inst->getOperand(2))->getMetadata();
auto *MDExp = cast<DIExpression>(MD2);
EXPECT_EQ(MDExp->getNumElements(), 0u);
}
} // end namespace } // end namespace