Skip to content

Commit

Permalink
[SPIRV] Emitting DebugSource, DebugCompileUnit (#97558)
Browse files Browse the repository at this point in the history
This commit introduces emission of DebugSource, DebugCompileUnit from
NonSemantic.Shader.DebugInfo.100 and required OpString with filename.
NonSemantic.Shader.DebugInfo.100 is divided, following DWARF into two
main concepts – emitting DIE and Line.
In DWARF .debug_abbriev and .debug_info sections are responsible for
emitting tree with information (DEIs) about e.g. types, compilation
unit. Corresponding to that in NonSemantic.Shader.DebugInfo.100 have
instructions like DebugSource, DebugCompileUnit etc. which preforms same
role in SPIR-V file. The difference is in fact that in SPIR-V there are
no sections but logical layout which forces order of the instruction
emission.
The NonSemantic.Shader.DebugInfo.100 requires for this type of global
information to be emitted after OpTypeXXX and OpConstantXXX
instructions.
One of the goals was to minimize changes and interaction with
SPIRVModuleAnalysis as possible which current commit achieves by
emitting it’s instructions directly into MachineFunction.
The possibility of duplicates are mitigated by guard inside pass which
emits the global information only once in one function.
By that method duplicates don’t have chance to be emitted.
From that point, adding new debug global instructions should be
straightforward.
  • Loading branch information
bwlodarcz authored Aug 23, 2024
1 parent 1519451 commit 62da359
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 7 deletions.
8 changes: 7 additions & 1 deletion llvm/docs/SPIRVUsage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ Static Compiler Commands
Command: `llc -O1 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_arbitrary_precision_integers input.ll -o output.spvt`
Description: Compiles an LLVM IL file to SPIR-V with (`-O1`) optimizations, targeting a 64-bit architecture. It enables the SPV_INTEL_arbitrary_precision_integers extension.

3. **SPIR-V Binary Generation**
3. **Compilation with experimental NonSemantic.Shader.DebugInfo.100 support**
Command: `llc --spv-emit-nonsemantic-debug-info --spirv-ext=+SPV_KHR_non_semantic_info input.ll -o output.spvt`
Description: Compiles an LLVM IL file to SPIR-V with additional NonSemantic.Shader.DebugInfo.100 instructions. It enables the required SPV_KHR_non_semantic_info extension.

4. **SPIR-V Binary Generation**
Command: `llc -O0 -mtriple=spirv64-unknown-unknown -filetype=obj input.ll -o output.spvt`
Description: Generates a SPIR-V object file (`output.spvt`) from an LLVM module, targeting a 64-bit SPIR-V architecture with no optimizations.

Expand Down Expand Up @@ -181,6 +185,8 @@ list of supported SPIR-V extensions, sorted alphabetically by their extension na
- Adds a new instruction that enables rotating values across invocations within a subgroup.
* - ``SPV_KHR_uniform_group_instructions``
- Allows support for additional group operations within uniform control flow.
* - ``SPV_KHR_non_semantic_info``
- Adds the ability to declare extended instruction sets that have no semantic impact and can be safely removed from a module.

To enable multiple extensions, list them separated by spaces. For example, to enable support for atomic operations on floating-point numbers and arbitrary precision integers, use:

Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/SPIRV/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ add_llvm_target(SPIRVCodeGen
SPIRVSubtarget.cpp
SPIRVTargetMachine.cpp
SPIRVUtils.cpp
SPIRVEmitNonSemanticDI.cpp

LINK_COMPONENTS
Analysis
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRV.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ FunctionPass *createSPIRVRegularizerPass();
FunctionPass *createSPIRVPreLegalizerPass();
FunctionPass *createSPIRVPostLegalizerPass();
ModulePass *createSPIRVEmitIntrinsicsPass(SPIRVTargetMachine *TM);
MachineFunctionPass *createSPIRVEmitNonSemanticDIPass(SPIRVTargetMachine *TM);
InstructionSelector *
createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,
const SPIRVSubtarget &Subtarget,
Expand All @@ -36,6 +37,7 @@ void initializeSPIRVConvergenceRegionAnalysisWrapperPassPass(PassRegistry &);
void initializeSPIRVPreLegalizerPass(PassRegistry &);
void initializeSPIRVPostLegalizerPass(PassRegistry &);
void initializeSPIRVEmitIntrinsicsPass(PassRegistry &);
void initializeSPIRVEmitNonSemanticDIPass(PassRegistry &);
} // namespace llvm

#endif // LLVM_LIB_TARGET_SPIRV_SPIRV_H
8 changes: 6 additions & 2 deletions llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ void SPIRVAsmPrinter::outputDebugSourceAndStrings(const Module &M) {
addStringImm(Str.first(), Inst);
outputMCInst(Inst);
}
// Output OpString.
outputModuleSection(SPIRV::MB_DebugStrings);
// Output OpSource.
MCInst Inst;
Inst.setOpcode(SPIRV::OpSource);
Expand Down Expand Up @@ -589,9 +591,11 @@ void SPIRVAsmPrinter::outputModuleSections() {
// the first section to allow use of: OpLine and OpNoLine debug information;
// non-semantic instructions with OpExtInst.
outputModuleSection(SPIRV::MB_TypeConstVars);
// 10. All function declarations (functions without a body).
// 10. All global NonSemantic.Shader.DebugInfo.100 instructions.
outputModuleSection(SPIRV::MB_NonSemanticGlobalDI);
// 11. All function declarations (functions without a body).
outputExtFuncDecls();
// 11. All function definitions (functions with a body).
// 12. All function definitions (functions with a body).
// This is done in regular function output.
}

Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ static const std::map<std::string, SPIRV::Extension::Extension>
SPIRV::Extension::Extension::SPV_KHR_shader_clock},
{"SPV_KHR_cooperative_matrix",
SPIRV::Extension::Extension::SPV_KHR_cooperative_matrix},
};
{"SPV_KHR_non_semantic_info",
SPIRV::Extension::Extension::SPV_KHR_non_semantic_info}};

bool SPIRVExtensionsParser::parse(cl::Option &O, llvm::StringRef ArgName,
llvm::StringRef ArgValue,
Expand Down
188 changes: 188 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#include "MCTargetDesc/SPIRVBaseInfo.h"
#include "MCTargetDesc/SPIRVMCTargetDesc.h"
#include "SPIRVGlobalRegistry.h"
#include "SPIRVRegisterInfo.h"
#include "SPIRVTargetMachine.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/Metadata.h"
#include "llvm/PassRegistry.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Path.h"

#define DEBUG_TYPE "spirv-nonsemantic-debug-info"

namespace llvm {
struct SPIRVEmitNonSemanticDI : public MachineFunctionPass {
static char ID;
SPIRVTargetMachine *TM;
SPIRVEmitNonSemanticDI(SPIRVTargetMachine *TM);
SPIRVEmitNonSemanticDI();

bool runOnMachineFunction(MachineFunction &MF) override;

private:
bool IsGlobalDIEmitted = false;
bool emitGlobalDI(MachineFunction &MF);
};

void initializeSPIRVEmitNonSemanticDIPass(PassRegistry &);

FunctionPass *createSPIRVEmitNonSemanticDIPass(SPIRVTargetMachine *TM) {
return new SPIRVEmitNonSemanticDI(TM);
}
} // namespace llvm

using namespace llvm;

INITIALIZE_PASS(SPIRVEmitNonSemanticDI, DEBUG_TYPE,
"SPIRV NonSemantic.Shader.DebugInfo.100 emitter", false, false)

char SPIRVEmitNonSemanticDI::ID = 0;

SPIRVEmitNonSemanticDI::SPIRVEmitNonSemanticDI(SPIRVTargetMachine *TM)
: MachineFunctionPass(ID), TM(TM) {
initializeSPIRVEmitNonSemanticDIPass(*PassRegistry::getPassRegistry());
}

SPIRVEmitNonSemanticDI::SPIRVEmitNonSemanticDI() : MachineFunctionPass(ID) {
initializeSPIRVEmitNonSemanticDIPass(*PassRegistry::getPassRegistry());
}

bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF) {
// If this MachineFunction doesn't have any BB repeat procedure
// for the next
if (MF.begin() == MF.end()) {
IsGlobalDIEmitted = false;
return false;
}

// Required variables to get from metadata search
LLVMContext *Context;
SmallString<128> FilePath;
unsigned SourceLanguage = 0;
int64_t DwarfVersion = 0;
int64_t DebugInfoVersion = 0;

// Searching through the Module metadata to find nescessary
// information like DwarfVersion or SourceLanguage
{
const MachineModuleInfo &MMI =
getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
const Module *M = MMI.getModule();
Context = &M->getContext();
const NamedMDNode *DbgCu = M->getNamedMetadata("llvm.dbg.cu");
if (!DbgCu)
return false;
for (const auto *Op : DbgCu->operands()) {
if (const auto *CompileUnit = dyn_cast<DICompileUnit>(Op)) {
DIFile *File = CompileUnit->getFile();
sys::path::append(FilePath, File->getDirectory(), File->getFilename());
SourceLanguage = CompileUnit->getSourceLanguage();
break;
}
}
const NamedMDNode *ModuleFlags = M->getNamedMetadata("llvm.module.flags");
for (const auto *Op : ModuleFlags->operands()) {
const MDOperand &MaybeStrOp = Op->getOperand(1);
if (MaybeStrOp.equalsStr("Dwarf Version"))
DwarfVersion =
cast<ConstantInt>(
cast<ConstantAsMetadata>(Op->getOperand(2))->getValue())
->getSExtValue();
else if (MaybeStrOp.equalsStr("Debug Info Version"))
DebugInfoVersion =
cast<ConstantInt>(
cast<ConstantAsMetadata>(Op->getOperand(2))->getValue())
->getSExtValue();
}
}
// NonSemantic.Shader.DebugInfo.100 global DI instruction emitting
{
// Required LLVM variables for emitting logic
const SPIRVInstrInfo *TII = TM->getSubtargetImpl()->getInstrInfo();
const SPIRVRegisterInfo *TRI = TM->getSubtargetImpl()->getRegisterInfo();
const RegisterBankInfo *RBI = TM->getSubtargetImpl()->getRegBankInfo();
SPIRVGlobalRegistry *GR = TM->getSubtargetImpl()->getSPIRVGlobalRegistry();
MachineRegisterInfo &MRI = MF.getRegInfo();
MachineBasicBlock &MBB = *MF.begin();

// To correct placement of a OpLabel instruction during SPIRVAsmPrinter
// emission all new instructions needs to be placed after OpFunction
// and before first terminator
MachineIRBuilder MIRBuilder(MBB, MBB.getFirstTerminator());

// Emit OpString with FilePath which is required by DebugSource
const Register StrReg = MRI.createVirtualRegister(&SPIRV::IDRegClass);
MRI.setType(StrReg, LLT::scalar(32));
MachineInstrBuilder MIB = MIRBuilder.buildInstr(SPIRV::OpString);
MIB.addDef(StrReg);
addStringImm(FilePath, MIB);

const SPIRVType *VoidTy =
GR->getOrCreateSPIRVType(Type::getVoidTy(*Context), MIRBuilder);

// Emit DebugSource which is required by DebugCompilationUnit
const Register DebugSourceResIdReg =
MRI.createVirtualRegister(&SPIRV::IDRegClass);
MRI.setType(DebugSourceResIdReg, LLT::scalar(32));
MIB = MIRBuilder.buildInstr(SPIRV::OpExtInst)
.addDef(DebugSourceResIdReg)
.addUse(GR->getSPIRVTypeID(VoidTy))
.addImm(static_cast<int64_t>(
SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100))
.addImm(SPIRV::NonSemanticExtInst::DebugSource)
.addUse(StrReg);
MIB.constrainAllUses(*TII, *TRI, *RBI);
GR->assignSPIRVTypeToVReg(VoidTy, DebugSourceResIdReg, MF);

const SPIRVType *I32Ty =
GR->getOrCreateSPIRVType(Type::getInt32Ty(*Context), MIRBuilder);

// Convert DwarfVersion, DebugInfo and SourceLanguage integers to OpConstant
// instructions required by DebugCompilationUnit
const Register DwarfVersionReg =
GR->buildConstantInt(DwarfVersion, MIRBuilder, I32Ty, false);
const Register DebugInfoVersionReg =
GR->buildConstantInt(DebugInfoVersion, MIRBuilder, I32Ty, false);
const Register SourceLanguageReg =
GR->buildConstantInt(SourceLanguage, MIRBuilder, I32Ty, false);

// Emit DebugCompilationUnit
const Register DebugCompUnitResIdReg =
MRI.createVirtualRegister(&SPIRV::IDRegClass);
MRI.setType(DebugCompUnitResIdReg, LLT::scalar(32));
MIB = MIRBuilder.buildInstr(SPIRV::OpExtInst)
.addDef(DebugCompUnitResIdReg)
.addUse(GR->getSPIRVTypeID(VoidTy))
.addImm(static_cast<int64_t>(
SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100))
.addImm(SPIRV::NonSemanticExtInst::DebugCompilationUnit)
.addUse(DebugInfoVersionReg)
.addUse(DwarfVersionReg)
.addUse(DebugSourceResIdReg)
.addUse(SourceLanguageReg);
MIB.constrainAllUses(*TII, *TRI, *RBI);
GR->assignSPIRVTypeToVReg(VoidTy, DebugCompUnitResIdReg, MF);
}
return true;
}

bool SPIRVEmitNonSemanticDI::runOnMachineFunction(MachineFunction &MF) {
bool Res = false;
// emitGlobalDI needs to be executed only once to avoid
// emitting duplicates
if (!IsGlobalDIEmitted) {
IsGlobalDIEmitted = true;
Res = emitGlobalDI(MF);
}
return Res;
}
23 changes: 21 additions & 2 deletions llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
#include "SPIRVSubtarget.h"
#include "SPIRVTargetMachine.h"
#include "SPIRVUtils.h"
#include "TargetInfo/SPIRVTargetInfo.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/TargetPassConfig.h"
Expand Down Expand Up @@ -427,7 +426,19 @@ void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
if (MAI.getSkipEmission(&MI))
continue;
const unsigned OpCode = MI.getOpcode();
if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) {
if (OpCode == SPIRV::OpString) {
collectOtherInstr(MI, MAI, SPIRV::MB_DebugStrings, IS);
} else if (OpCode == SPIRV::OpExtInst) {
MachineOperand Ins = MI.getOperand(3);
namespace NS = SPIRV::NonSemanticExtInst;
static constexpr int64_t GlobalNonSemanticDITy[] = {
NS::DebugSource, NS::DebugCompilationUnit};
bool IsGlobalDI = false;
for (unsigned Idx = 0; Idx < std::size(GlobalNonSemanticDITy); ++Idx)
IsGlobalDI |= Ins.getImm() == GlobalNonSemanticDITy[Idx];
if (IsGlobalDI)
collectOtherInstr(MI, MAI, SPIRV::MB_NonSemanticGlobalDI, IS);
} else if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) {
collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames, IS);
} else if (OpCode == SPIRV::OpEntryPoint) {
collectOtherInstr(MI, MAI, SPIRV::MB_EntryPoints, IS);
Expand Down Expand Up @@ -899,6 +910,14 @@ void addInstrRequirements(const MachineInstr &MI,
Reqs.addCapability(SPIRV::Capability::Float16Buffer);
break;
}
case SPIRV::OpExtInst: {
if (MI.getOperand(2).getImm() ==
static_cast<int64_t>(
SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100)) {
Reqs.addExtension(SPIRV::Extension::SPV_KHR_non_semantic_info);
}
break;
}
case SPIRV::OpBitReverse:
case SPIRV::OpBitFieldInsert:
case SPIRV::OpBitFieldSExtract:
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"

namespace llvm {
class SPIRVSubtarget;
Expand All @@ -34,9 +33,11 @@ enum ModuleSectionType {
MB_EntryPoints, // All OpEntryPoint instructions (if any).
// MB_ExecutionModes, MB_DebugSourceAndStrings,
MB_DebugNames, // All OpName and OpMemberName intrs.
MB_DebugStrings, // All OpString intrs.
MB_DebugModuleProcessed, // All OpModuleProcessed instructions.
MB_Annotations, // OpDecorate, OpMemberDecorate etc.
MB_TypeConstVars, // OpTypeXXX, OpConstantXXX, and global OpVariables.
MB_NonSemanticGlobalDI, // OpExtInst with e.g. DebugSource, DebugTypeBasic.
MB_ExtFuncDecls, // OpFunction etc. to declare for external funcs.
NUM_MODULE_SECTIONS // Total number of sections requiring basic blocks.
};
Expand Down
12 changes: 12 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ class SPIRVPassConfig : public TargetPassConfig {
void addOptimizedRegAlloc() override {}

void addPostRegAlloc() override;
void addPreEmitPass() override;

private:
const SPIRVTargetMachine &TM;
Expand Down Expand Up @@ -208,6 +209,17 @@ bool SPIRVPassConfig::addRegBankSelect() {
return false;
}

static cl::opt<bool> SPVEnableNonSemanticDI(
"spv-emit-nonsemantic-debug-info",
cl::desc("Emit SPIR-V NonSemantic.Shader.DebugInfo.100 instructions"),
cl::Optional, cl::init(false));

void SPIRVPassConfig::addPreEmitPass() {
if (SPVEnableNonSemanticDI) {
addPass(createSPIRVEmitNonSemanticDIPass(&getTM<SPIRVTargetMachine>()));
}
}

namespace {
// A custom subclass of InstructionSelect, which is mostly the same except from
// not requiring RegBankSelect to occur previously.
Expand Down
Loading

0 comments on commit 62da359

Please sign in to comment.