CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
297 lines
12 KiB
C++
297 lines
12 KiB
C++
//===-- NVPTXDwarfDebug.cpp - NVPTX DwarfDebug Implementation ------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements helper functions for NVPTX-specific debug information
|
|
// processing.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "NVPTXDwarfDebug.h"
|
|
#include "NVPTXSubtarget.h"
|
|
#include "llvm/BinaryFormat/Dwarf.h"
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
#include "llvm/CodeGen/MachineInstr.h"
|
|
#include "llvm/IR/DebugInfoMetadata.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/GlobalVariable.h"
|
|
#include "llvm/MC/MCAsmInfo.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/NVPTXAddrSpace.h"
|
|
#include "llvm/Target/TargetMachine.h"
|
|
|
|
using namespace llvm;
|
|
|
|
// Command line option to control inlined_at enhancement to lineinfo support.
|
|
// Valid only when debuginfo emissionkind is DebugDirectivesOnly or
|
|
// LineTablesOnly.
|
|
static cl::opt<bool> LineInfoWithInlinedAt(
|
|
"line-info-inlined-at",
|
|
cl::desc("Emit line with inlined_at enhancement for NVPTX"), cl::init(true),
|
|
cl::Hidden);
|
|
|
|
NVPTXDwarfDebug::NVPTXDwarfDebug(AsmPrinter *A) : DwarfDebug(A) {
|
|
// PTX emits debug strings inline (no .debug_str section), does not support
|
|
// .debug_ranges, and uses sections as references (no temp symbols inside
|
|
// DWARF sections). DWARF v2 is the default for NVPTX and does not support
|
|
// accelerator tables.
|
|
setUseInlineStrings(true);
|
|
setUseRangesSection(false);
|
|
setUseSectionsAsReferences(true);
|
|
Asm->OutStreamer->getContext().setDwarfVersion(2);
|
|
setTheAccelTableKind(AccelTableKind::None);
|
|
}
|
|
|
|
MCSymbol *NVPTXDwarfDebug::getOrCreateFuncNameSymbol(StringRef LinkageName) {
|
|
return InfoHolder.getStringPool().getEntry(*Asm, LinkageName).getSymbol();
|
|
}
|
|
|
|
bool NVPTXDwarfDebug::isEnhancedLineinfo(const MachineFunction &MF) const {
|
|
const DISubprogram *SP = MF.getFunction().getSubprogram();
|
|
const NVPTXSubtarget &STI = MF.getSubtarget<NVPTXSubtarget>();
|
|
return LineInfoWithInlinedAt && (STI.getPTXVersion() >= 72) && SP &&
|
|
(SP->getUnit()->isDebugDirectivesOnly() ||
|
|
SP->getUnit()->getEmissionKind() == DICompileUnit::LineTablesOnly);
|
|
}
|
|
|
|
/// NVPTX-specific source line recording with inlined_at support.
|
|
///
|
|
/// Why this exists:
|
|
/// NVPTX supports an "enhanced lineinfo" mode where inlining context is carried
|
|
/// via line-table directives, rather than full DWARF DIEs. This is conceptually
|
|
/// similar to proposals[1] for richer DWARF line tables that carry inline call
|
|
/// context and callee identity in the line table. NVPTX implements this via
|
|
/// target-specific `.loc` extensions in the PTX ISA[3].
|
|
///
|
|
/// How it impacts PTX assembly generation:
|
|
/// - When enabled (PTX ISA >= 7.2 + line-tables-only / debug-directives-only),
|
|
/// we emit multiple consecutive `.loc` directives for a single inlined
|
|
/// instruction: the instruction's own location and its `inlined_at` parent
|
|
/// chain.
|
|
/// - During emission we use `MCStreamer::emitDwarfLocDirectiveWithInlinedAt` to
|
|
/// emit an enhanced `.loc` directive[3] that carries the extra
|
|
/// `function_name` and `inlined_at` operands in the PTX assembly stream.
|
|
///
|
|
/// Example (conceptual PTX `.loc` sequence for an inlined callsite):
|
|
/// .loc 1 16 3 // caller location
|
|
/// .loc 1 5 3, function_name $L__info_stringN, inlined_at 1 16 3
|
|
/// // inlined callee location
|
|
/// Here, $L__info_stringN is a label (or label+immediate) referring into
|
|
/// `.debug_str`.
|
|
///
|
|
/// How this impacts DWARF :
|
|
/// DWARF generation tools that consume this PTX(e.g. ptxas assembler) can use
|
|
/// the `inlined_at` and `function_name` operands to extend the DWARF v2
|
|
/// line table information.
|
|
/// This adds:
|
|
/// - a `context` column[2]: the `inlined_at <file> <line> <col>` information
|
|
/// populates an inlining "context" (a reference to the parent/callsite row)
|
|
/// enabling reconstruction of inline call chains from the line table.
|
|
/// - a `function_name` column[2]: the `.loc ... function_name <sym>` identifies
|
|
/// the inlined callee associated with a non-zero context.
|
|
///
|
|
/// References:
|
|
/// - [1] DWARF line tables / Two-Level Line Tables:
|
|
/// https://wiki.dwarfstd.org/TwoLevelLineTables.md
|
|
/// - [2] DWARF issue tracking for Two-Level Line Tables:
|
|
/// https://dwarfstd.org/issues/140906.1.html
|
|
/// - [3] NVIDIA PTX ISA `.loc` (debugging directives; PTX ISA 7.2+):
|
|
/// https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#debugging-directives-loc
|
|
void NVPTXDwarfDebug::recordTargetSourceLine(const DebugLoc &DL,
|
|
unsigned Flags) {
|
|
// Maintain a work list of .loc to be emitted. If we are emitting the
|
|
// inlined_at directive, we might need to emit additional .loc prior
|
|
// to it for the location contained in the inlined_at.
|
|
SmallVector<const DILocation *, 8> WorkList;
|
|
SmallDenseSet<const DILocation *, 8> WorkListSet;
|
|
const DILocation *EmitLoc = DL.get();
|
|
|
|
if (!EmitLoc)
|
|
return;
|
|
|
|
const MachineFunction *MF = Asm->MF;
|
|
if (!MF)
|
|
return;
|
|
|
|
const bool EnhancedLineinfo = isEnhancedLineinfo(*MF);
|
|
|
|
while (EmitLoc) {
|
|
// Get the scope for the current location.
|
|
const DIScope *Scope = EmitLoc->getScope();
|
|
if (!Scope)
|
|
break; // scope is null, we are done.
|
|
|
|
// Check if this loc is already in work list, if so, we are done.
|
|
if (WorkListSet.contains(EmitLoc))
|
|
break;
|
|
|
|
// Add this location to the work list.
|
|
WorkList.push_back(EmitLoc);
|
|
WorkListSet.insert(EmitLoc);
|
|
|
|
if (!EnhancedLineinfo) // No enhanced lineinfo, we are done.
|
|
break;
|
|
|
|
const DILocation *IA = EmitLoc->getInlinedAt();
|
|
// Check if this has inlined_at information, and if the parent location
|
|
// has not yet been emitted. If already emitted, we don't need to
|
|
// re-emit the parent chain.
|
|
if (IA && !EmittedInlinedAtLocs.contains(IA))
|
|
EmitLoc = IA;
|
|
else // We are done.
|
|
break;
|
|
}
|
|
|
|
const unsigned CUID = Asm->OutStreamer->getContext().getDwarfCompileUnitID();
|
|
// Traverse the work list, and emit .loc.
|
|
while (!WorkList.empty()) {
|
|
const DILocation *Current = WorkList.pop_back_val();
|
|
const DIScope *Scope = Current->getScope();
|
|
|
|
if (!Scope)
|
|
llvm_unreachable("we shouldn't be here for null scope");
|
|
|
|
const DILocation *InlinedAt = Current->getInlinedAt();
|
|
StringRef Fn = Scope->getFilename();
|
|
const unsigned Line = Current->getLine();
|
|
const unsigned Col = Current->getColumn();
|
|
unsigned Discriminator = 0;
|
|
if (Line != 0 && getDwarfVersion() >= 4)
|
|
if (const DILexicalBlockFile *LBF = dyn_cast<DILexicalBlockFile>(Scope))
|
|
Discriminator = LBF->getDiscriminator();
|
|
|
|
const unsigned FileNo = static_cast<DwarfCompileUnit &>(*getUnits()[CUID])
|
|
.getOrCreateSourceID(Scope->getFile());
|
|
|
|
if (EnhancedLineinfo && InlinedAt) {
|
|
const unsigned FileIA = static_cast<DwarfCompileUnit &>(*getUnits()[CUID])
|
|
.getOrCreateSourceID(InlinedAt->getFile());
|
|
const DISubprogram *SubProgram = getDISubprogram(Current->getScope());
|
|
DwarfStringPoolEntryRef Entry = InfoHolder.getStringPool().getEntry(
|
|
*Asm, SubProgram->getLinkageName());
|
|
Asm->OutStreamer->emitDwarfLocDirectiveWithInlinedAt(
|
|
FileNo, Line, Col, FileIA, InlinedAt->getLine(),
|
|
InlinedAt->getColumn(), Entry.getSymbol(), Flags, 0, Discriminator,
|
|
Fn);
|
|
} else {
|
|
Asm->OutStreamer->emitDwarfLocDirective(FileNo, Line, Col, Flags, 0,
|
|
Discriminator, Fn);
|
|
}
|
|
// Mark this location as emitted so we don't re-emit the parent chain
|
|
// for subsequent instructions that share the same inlined_at parent.
|
|
if (EnhancedLineinfo)
|
|
EmittedInlinedAtLocs.insert(Current);
|
|
}
|
|
}
|
|
|
|
/// NVPTX-specific debug info initialization.
|
|
void NVPTXDwarfDebug::initializeTargetDebugInfo(const MachineFunction &MF) {
|
|
EmittedInlinedAtLocs.clear();
|
|
}
|
|
|
|
// PTX does not support subtracting labels from the code section in the
|
|
// debug_loc section. To work around this, the NVPTX backend needs the
|
|
// compile unit to have no low_pc in order to have a zero base_address
|
|
// when handling debug_loc in cuda-gdb.
|
|
bool NVPTXDwarfDebug::shouldAttachCompileUnitRanges() const {
|
|
return !tuneForGDB();
|
|
}
|
|
|
|
// Same label-subtraction limitation as above: cuda-gdb doesn't handle
|
|
// setting a per-variable base to zero, so we emit labels with no base
|
|
// while having no compile unit low_pc.
|
|
bool NVPTXDwarfDebug::shouldResetBaseAddress(const MCSection &Section) const {
|
|
return tuneForGDB();
|
|
}
|
|
|
|
static unsigned translateToNVVMDWARFAddrSpace(unsigned AddrSpace) {
|
|
switch (AddrSpace) {
|
|
case NVPTXAS::ADDRESS_SPACE_GENERIC:
|
|
return NVPTXAS::DWARF_ADDR_generic_space;
|
|
case NVPTXAS::ADDRESS_SPACE_GLOBAL:
|
|
return NVPTXAS::DWARF_ADDR_global_space;
|
|
case NVPTXAS::ADDRESS_SPACE_SHARED:
|
|
return NVPTXAS::DWARF_ADDR_shared_space;
|
|
case NVPTXAS::ADDRESS_SPACE_CONST:
|
|
return NVPTXAS::DWARF_ADDR_const_space;
|
|
case NVPTXAS::ADDRESS_SPACE_LOCAL:
|
|
return NVPTXAS::DWARF_ADDR_local_space;
|
|
default:
|
|
llvm_unreachable(
|
|
"Cannot translate unknown address space to DWARF address space");
|
|
return AddrSpace;
|
|
}
|
|
}
|
|
|
|
// cuda-gdb requires DW_AT_address_class on variable DIEs. The address space
|
|
// is encoded in the DIExpression as a DW_OP_constu <DWARF Address Space>
|
|
// DW_OP_swap DW_OP_xderef sequence. We strip that sequence from the
|
|
// expression and return the address space so the caller can emit
|
|
// DW_AT_address_class separately.
|
|
const DIExpression *NVPTXDwarfDebug::adjustExpressionForTarget(
|
|
const DIExpression *Expr, std::optional<unsigned> &TargetAddrSpace) const {
|
|
if (!tuneForGDB())
|
|
return Expr;
|
|
unsigned LocalAddrSpace;
|
|
const DIExpression *NewExpr =
|
|
DIExpression::extractAddressClass(Expr, LocalAddrSpace);
|
|
if (NewExpr != Expr) {
|
|
TargetAddrSpace = LocalAddrSpace;
|
|
return NewExpr;
|
|
}
|
|
return Expr;
|
|
}
|
|
|
|
// Emit DW_AT_address_class for cuda-gdb. See NVPTXAS::DWARF_AddressSpace.
|
|
//
|
|
// The address class depends on the variable's storage kind:
|
|
// Global: from the expression (if encoded) or the IR address space
|
|
// Register: DWARF_ADDR_reg_space (no expression means register location)
|
|
// FrameIndex: from the expression (if encoded) or DWARF_ADDR_local_space
|
|
void NVPTXDwarfDebug::addTargetVariableAttributes(
|
|
DwarfCompileUnit &CU, DIE &Die, std::optional<unsigned> TargetAddrSpace,
|
|
VariableLocationKind VarLocKind, const GlobalVariable *GV) const {
|
|
if (!tuneForGDB())
|
|
return;
|
|
|
|
unsigned DefaultAddrSpace = NVPTXAS::DWARF_ADDR_global_space;
|
|
switch (VarLocKind) {
|
|
case VariableLocationKind::Global:
|
|
if (!TargetAddrSpace && GV)
|
|
TargetAddrSpace =
|
|
translateToNVVMDWARFAddrSpace(GV->getType()->getAddressSpace());
|
|
DefaultAddrSpace = NVPTXAS::DWARF_ADDR_global_space;
|
|
break;
|
|
case VariableLocationKind::Register:
|
|
DefaultAddrSpace = NVPTXAS::DWARF_ADDR_reg_space;
|
|
break;
|
|
case VariableLocationKind::FrameIndex:
|
|
DefaultAddrSpace = NVPTXAS::DWARF_ADDR_local_space;
|
|
break;
|
|
}
|
|
|
|
CU.addUInt(Die, dwarf::DW_AT_address_class, dwarf::DW_FORM_data1,
|
|
TargetAddrSpace.value_or(DefaultAddrSpace));
|
|
}
|
|
|
|
void NVPTXDwarfDebug::finishTargetUnitAttributes(const DICompileUnit &DIUnit,
|
|
DwarfCompileUnit &NewCU) {
|
|
uint16_t Dialect = DIUnit.getSourceLanguage().getDialect();
|
|
// A zero dialect means "no dialect specified"; nothing to emit. This field
|
|
// should have already been range-checked by the assembly parser, IR verifier,
|
|
// and bitcode reader.
|
|
assert(Dialect <= dwarf::DW_LLVM_LANG_DIALECT_max &&
|
|
"Invalid or unsupported NVPTX language dialect.");
|
|
if (Dialect == 0)
|
|
return;
|
|
|
|
NewCU.addUInt(NewCU.getUnitDie(), dwarf::DW_AT_LLVM_language_dialect,
|
|
dwarf::DW_FORM_data1, Dialect);
|
|
}
|