LLVM 23.0.0git
NVPTXDwarfDebug.cpp
Go to the documentation of this file.
1//===-- NVPTXDwarfDebug.cpp - NVPTX DwarfDebug Implementation ------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements helper functions for NVPTX-specific debug information
10// processing.
11//
12//===----------------------------------------------------------------------===//
13
14#include "NVPTXDwarfDebug.h"
15#include "NVPTXSubtarget.h"
20#include "llvm/IR/Function.h"
22#include "llvm/MC/MCAsmInfo.h"
23#include "llvm/MC/MCContext.h"
24#include "llvm/MC/MCStreamer.h"
28
29using namespace llvm;
30
31// Command line option to control inlined_at enhancement to lineinfo support.
32// Valid only when debuginfo emissionkind is DebugDirectivesOnly or
33// LineTablesOnly.
35 "line-info-inlined-at",
36 cl::desc("Emit line with inlined_at enhancement for NVPTX"), cl::init(true),
38
40 // PTX emits debug strings inline (no .debug_str section), does not support
41 // .debug_ranges, and uses sections as references (no temp symbols inside
42 // DWARF sections). DWARF v2 is the default for NVPTX and does not support
43 // accelerator tables.
47 Asm->OutStreamer->getContext().setDwarfVersion(2);
49}
50
51/// NVPTX-specific source line recording with inlined_at support.
52///
53/// Why this exists:
54/// NVPTX supports an "enhanced lineinfo" mode where inlining context is carried
55/// via line-table directives, rather than full DWARF DIEs. This is conceptually
56/// similar to proposals[1] for richer DWARF line tables that carry inline call
57/// context and callee identity in the line table. NVPTX implements this via
58/// target-specific `.loc` extensions in the PTX ISA[3].
59///
60/// How it impacts PTX assembly generation:
61/// - When enabled (PTX ISA >= 7.2 + line-tables-only / debug-directives-only),
62/// we emit multiple consecutive `.loc` directives for a single inlined
63/// instruction: the instruction's own location and its `inlined_at` parent
64/// chain.
65/// - During emission we use `MCStreamer::emitDwarfLocDirectiveWithInlinedAt` to
66/// emit an enhanced `.loc` directive[3] that carries the extra
67/// `function_name` and `inlined_at` operands in the PTX assembly stream.
68///
69/// Example (conceptual PTX `.loc` sequence for an inlined callsite):
70/// .loc 1 16 3 // caller location
71/// .loc 1 5 3, function_name $L__info_stringN, inlined_at 1 16 3
72/// // inlined callee location
73/// Here, $L__info_stringN is a label (or label+immediate) referring into
74/// `.debug_str`.
75///
76/// How this impacts DWARF :
77/// DWARF generation tools that consume this PTX(e.g. ptxas assembler) can use
78/// the `inlined_at` and `function_name` operands to extend the DWARF v2
79/// line table information.
80/// This adds:
81/// - a `context` column[2]: the `inlined_at <file> <line> <col>` information
82/// populates an inlining "context" (a reference to the parent/callsite row)
83/// enabling reconstruction of inline call chains from the line table.
84/// - a `function_name` column[2]: the `.loc ... function_name <sym>` identifies
85/// the inlined callee associated with a non-zero context.
86///
87/// References:
88/// - [1] DWARF line tables / Two-Level Line Tables:
89/// https://wiki.dwarfstd.org/TwoLevelLineTables.md
90/// - [2] DWARF issue tracking for Two-Level Line Tables:
91/// https://dwarfstd.org/issues/140906.1.html
92/// - [3] NVIDIA PTX ISA `.loc` (debugging directives; PTX ISA 7.2+):
93/// https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#debugging-directives-loc
95 unsigned Flags) {
96 // Maintain a work list of .loc to be emitted. If we are emitting the
97 // inlined_at directive, we might need to emit additional .loc prior
98 // to it for the location contained in the inlined_at.
101 const DILocation *EmitLoc = DL.get();
102
103 if (!EmitLoc)
104 return;
105
106 const MachineFunction *MF = Asm->MF;
107 if (!MF)
108 return;
109
110 const DISubprogram *SP = MF->getFunction().getSubprogram();
111 const NVPTXSubtarget &STI = MF->getSubtarget<NVPTXSubtarget>();
112 const bool EnhancedLineinfo =
113 LineInfoWithInlinedAt && (STI.getPTXVersion() >= 72) && SP &&
114 (SP->getUnit()->isDebugDirectivesOnly() ||
115 SP->getUnit()->getEmissionKind() == DICompileUnit::LineTablesOnly);
116
117 while (EmitLoc) {
118 // Get the scope for the current location.
119 const DIScope *Scope = EmitLoc->getScope();
120 if (!Scope)
121 break; // scope is null, we are done.
122
123 // Check if this loc is already in work list, if so, we are done.
124 if (WorkListSet.contains(EmitLoc))
125 break;
126
127 // Add this location to the work list.
128 WorkList.push_back(EmitLoc);
129 WorkListSet.insert(EmitLoc);
130
131 if (!EnhancedLineinfo) // No enhanced lineinfo, we are done.
132 break;
133
134 const DILocation *IA = EmitLoc->getInlinedAt();
135 // Check if this has inlined_at information, and if the parent location
136 // has not yet been emitted. If already emitted, we don't need to
137 // re-emit the parent chain.
138 if (IA && !EmittedInlinedAtLocs.contains(IA))
139 EmitLoc = IA;
140 else // We are done.
141 break;
142 }
143
144 const unsigned CUID = Asm->OutStreamer->getContext().getDwarfCompileUnitID();
145 // Traverse the work list, and emit .loc.
146 while (!WorkList.empty()) {
147 const DILocation *Current = WorkList.pop_back_val();
148 const DIScope *Scope = Current->getScope();
149
150 if (!Scope)
151 llvm_unreachable("we shouldn't be here for null scope");
152
153 const DILocation *InlinedAt = Current->getInlinedAt();
154 StringRef Fn = Scope->getFilename();
155 const unsigned Line = Current->getLine();
156 const unsigned Col = Current->getColumn();
157 unsigned Discriminator = 0;
158 if (Line != 0 && getDwarfVersion() >= 4)
159 if (const DILexicalBlockFile *LBF = dyn_cast<DILexicalBlockFile>(Scope))
160 Discriminator = LBF->getDiscriminator();
161
162 const unsigned FileNo = static_cast<DwarfCompileUnit &>(*getUnits()[CUID])
163 .getOrCreateSourceID(Scope->getFile());
164
165 if (EnhancedLineinfo && InlinedAt) {
166 const unsigned FileIA = static_cast<DwarfCompileUnit &>(*getUnits()[CUID])
167 .getOrCreateSourceID(InlinedAt->getFile());
168 const DISubprogram *SubProgram = getDISubprogram(Current->getScope());
169 DwarfStringPoolEntryRef Entry = InfoHolder.getStringPool().getEntry(
170 *Asm, SubProgram->getLinkageName());
171 Asm->OutStreamer->emitDwarfLocDirectiveWithInlinedAt(
172 FileNo, Line, Col, FileIA, InlinedAt->getLine(),
173 InlinedAt->getColumn(), Entry.getSymbol(), Flags, 0, Discriminator,
174 Fn);
175 } else {
176 Asm->OutStreamer->emitDwarfLocDirective(FileNo, Line, Col, Flags, 0,
177 Discriminator, Fn);
178 }
179 // Mark this location as emitted so we don't re-emit the parent chain
180 // for subsequent instructions that share the same inlined_at parent.
181 if (EnhancedLineinfo)
182 EmittedInlinedAtLocs.insert(Current);
183 }
184}
185
186/// NVPTX-specific debug info initialization.
188 EmittedInlinedAtLocs.clear();
189}
190
191// PTX does not support subtracting labels from the code section in the
192// debug_loc section. To work around this, the NVPTX backend needs the
193// compile unit to have no low_pc in order to have a zero base_address
194// when handling debug_loc in cuda-gdb.
198
199// Same label-subtraction limitation as above: cuda-gdb doesn't handle
200// setting a per-variable base to zero, so we emit labels with no base
201// while having no compile unit low_pc.
203 return tuneForGDB();
204}
205
206static unsigned translateToNVVMDWARFAddrSpace(unsigned AddrSpace) {
207 switch (AddrSpace) {
218 default:
220 "Cannot translate unknown address space to DWARF address space");
221 return AddrSpace;
222 }
223}
224
225// cuda-gdb requires DW_AT_address_class on variable DIEs. The address space
226// is encoded in the DIExpression as a DW_OP_constu <DWARF Address Space>
227// DW_OP_swap DW_OP_xderef sequence. We strip that sequence from the
228// expression and return the address space so the caller can emit
229// DW_AT_address_class separately.
231 const DIExpression *Expr, std::optional<unsigned> &TargetAddrSpace) const {
232 if (!tuneForGDB())
233 return Expr;
234 unsigned LocalAddrSpace;
235 const DIExpression *NewExpr =
236 DIExpression::extractAddressClass(Expr, LocalAddrSpace);
237 if (NewExpr != Expr) {
238 TargetAddrSpace = LocalAddrSpace;
239 return NewExpr;
240 }
241 return Expr;
242}
243
244// Emit DW_AT_address_class for cuda-gdb. See NVPTXAS::DWARF_AddressSpace.
245//
246// The address class depends on the variable's storage kind:
247// Global: from the expression (if encoded) or the IR address space
248// Register: DWARF_ADDR_reg_space (no expression means register location)
249// FrameIndex: from the expression (if encoded) or DWARF_ADDR_local_space
251 DwarfCompileUnit &CU, DIE &Die, std::optional<unsigned> TargetAddrSpace,
252 VariableLocationKind VarLocKind, const GlobalVariable *GV) const {
253 if (!tuneForGDB())
254 return;
255
256 unsigned DefaultAddrSpace = NVPTXAS::DWARF_ADDR_global_space;
257 switch (VarLocKind) {
259 if (!TargetAddrSpace && GV)
260 TargetAddrSpace =
262 DefaultAddrSpace = NVPTXAS::DWARF_ADDR_global_space;
263 break;
265 DefaultAddrSpace = NVPTXAS::DWARF_ADDR_reg_space;
266 break;
268 DefaultAddrSpace = NVPTXAS::DWARF_ADDR_local_space;
269 break;
270 }
271
272 CU.addUInt(Die, dwarf::DW_AT_address_class, dwarf::DW_FORM_data1,
273 TargetAddrSpace.value_or(DefaultAddrSpace));
274}
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
This file contains constants used for implementing Dwarf debug support.
NVPTX address space definition.
static unsigned translateToNVVMDWARFAddrSpace(unsigned AddrSpace)
static cl::opt< bool > LineInfoWithInlinedAt("line-info-inlined-at", cl::desc("Emit line with inlined_at enhancement for NVPTX"), cl::init(true), cl::Hidden)
This class is intended to be used as a driving class for all asm writers.
Definition AsmPrinter.h:91
A structured debug information entry.
Definition DIE.h:828
DWARF expression.
static LLVM_ABI const DIExpression * extractAddressClass(const DIExpression *Expr, unsigned &AddrClass)
Checks if the last 4 elements of the expression are DW_OP_constu <DWARFAddress Space> DW_OP_swap DW_O...
Base class for scope-like contexts.
Subprogram description. Uses SubclassData1.
AsmPrinter * Asm
Target of debug info emission.
A debug info location.
Definition DebugLoc.h:123
uint16_t getDwarfVersion() const
Returns the Dwarf Version.
VariableLocationKind
Describes the storage kind of a debug variable for target hooks.
Definition DwarfDebug.h:752
void setUseSectionsAsReferences(bool V)
Definition DwarfDebug.h:729
void setUseRangesSection(bool V)
Definition DwarfDebug.h:728
const SmallVectorImpl< std::unique_ptr< DwarfCompileUnit > > & getUnits()
Definition DwarfDebug.h:737
void setTheAccelTableKind(AccelTableKind K)
Seet TheAccelTableKind.
Definition DwarfDebug.h:864
DwarfFile InfoHolder
Holder for the file specific debug information.
Definition DwarfDebug.h:710
void setUseInlineStrings(bool V)
Setters for target-specific DWARF configuration overrides.
Definition DwarfDebug.h:727
DwarfDebug(AsmPrinter *A)
DwarfStringPoolEntryRef: Dwarf string pool entry reference.
DISubprogram * getSubprogram() const
Get the attached subprogram.
PointerType * getType() const
Global values are always pointers.
Instances of this class represent a uniqued identifier for a section in the current translation unit.
Definition MCSection.h:516
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
Function & getFunction()
Return the LLVM function that this machine code represents.
bool shouldResetBaseAddress(const MCSection &Section) const override
Whether the target requires resetting the base address in range/loc lists.
bool shouldAttachCompileUnitRanges() const override
Whether to attach ranges/low_pc to the compile unit DIE in endModule.
void recordTargetSourceLine(const DebugLoc &DL, unsigned Flags) override
NVPTX-specific source line recording with inlined_at support.
const DIExpression * adjustExpressionForTarget(const DIExpression *Expr, std::optional< unsigned > &TargetAddrSpace) const override
Extract target-specific address space information from a DIExpression.
NVPTXDwarfDebug(AsmPrinter *A)
void initializeTargetDebugInfo(const MachineFunction &MF) override
NVPTX-specific debug info initialization.
void addTargetVariableAttributes(DwarfCompileUnit &CU, DIE &Die, std::optional< unsigned > TargetAddrSpace, VariableLocationKind VarLocKind, const GlobalVariable *GV=nullptr) const override
Add target-specific attributes to a variable DIE (e.g.
unsigned getPTXVersion() const
unsigned getAddressSpace() const
Return the address space of the Pointer type.
Implements a dense probed hash-table based set with some number of buckets stored inline.
Definition DenseSet.h:291
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
std::pair< iterator, bool > insert(const ValueT &V)
Definition DenseSet.h:202
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
Definition DenseSet.h:175
bool tuneForGDB() const
Definition DwarfDebug.h:975
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
initializer< Ty > init(const Ty &Val)
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
LLVM_ABI DISubprogram * getDISubprogram(const MDNode *Scope)
Find subprogram that is enclosing this scope.