Skip to content

Commit 62da359

Browse files
authored
[SPIRV] Emitting DebugSource, DebugCompileUnit (#97558)
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.
1 parent 1519451 commit 62da359

10 files changed

+287
-7
lines changed

llvm/docs/SPIRVUsage.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ Static Compiler Commands
3333
Command: `llc -O1 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_INTEL_arbitrary_precision_integers input.ll -o output.spvt`
3434
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.
3535

36-
3. **SPIR-V Binary Generation**
36+
3. **Compilation with experimental NonSemantic.Shader.DebugInfo.100 support**
37+
Command: `llc --spv-emit-nonsemantic-debug-info --spirv-ext=+SPV_KHR_non_semantic_info input.ll -o output.spvt`
38+
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.
39+
40+
4. **SPIR-V Binary Generation**
3741
Command: `llc -O0 -mtriple=spirv64-unknown-unknown -filetype=obj input.ll -o output.spvt`
3842
Description: Generates a SPIR-V object file (`output.spvt`) from an LLVM module, targeting a 64-bit SPIR-V architecture with no optimizations.
3943

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

185191
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:
186192

llvm/lib/Target/SPIRV/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ add_llvm_target(SPIRVCodeGen
4040
SPIRVSubtarget.cpp
4141
SPIRVTargetMachine.cpp
4242
SPIRVUtils.cpp
43+
SPIRVEmitNonSemanticDI.cpp
4344

4445
LINK_COMPONENTS
4546
Analysis

llvm/lib/Target/SPIRV/SPIRV.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ FunctionPass *createSPIRVRegularizerPass();
2626
FunctionPass *createSPIRVPreLegalizerPass();
2727
FunctionPass *createSPIRVPostLegalizerPass();
2828
ModulePass *createSPIRVEmitIntrinsicsPass(SPIRVTargetMachine *TM);
29+
MachineFunctionPass *createSPIRVEmitNonSemanticDIPass(SPIRVTargetMachine *TM);
2930
InstructionSelector *
3031
createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,
3132
const SPIRVSubtarget &Subtarget,
@@ -36,6 +37,7 @@ void initializeSPIRVConvergenceRegionAnalysisWrapperPassPass(PassRegistry &);
3637
void initializeSPIRVPreLegalizerPass(PassRegistry &);
3738
void initializeSPIRVPostLegalizerPass(PassRegistry &);
3839
void initializeSPIRVEmitIntrinsicsPass(PassRegistry &);
40+
void initializeSPIRVEmitNonSemanticDIPass(PassRegistry &);
3941
} // namespace llvm
4042

4143
#endif // LLVM_LIB_TARGET_SPIRV_SPIRV_H

llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ void SPIRVAsmPrinter::outputDebugSourceAndStrings(const Module &M) {
274274
addStringImm(Str.first(), Inst);
275275
outputMCInst(Inst);
276276
}
277+
// Output OpString.
278+
outputModuleSection(SPIRV::MB_DebugStrings);
277279
// Output OpSource.
278280
MCInst Inst;
279281
Inst.setOpcode(SPIRV::OpSource);
@@ -589,9 +591,11 @@ void SPIRVAsmPrinter::outputModuleSections() {
589591
// the first section to allow use of: OpLine and OpNoLine debug information;
590592
// non-semantic instructions with OpExtInst.
591593
outputModuleSection(SPIRV::MB_TypeConstVars);
592-
// 10. All function declarations (functions without a body).
594+
// 10. All global NonSemantic.Shader.DebugInfo.100 instructions.
595+
outputModuleSection(SPIRV::MB_NonSemanticGlobalDI);
596+
// 11. All function declarations (functions without a body).
593597
outputExtFuncDecls();
594-
// 11. All function definitions (functions with a body).
598+
// 12. All function definitions (functions with a body).
595599
// This is done in regular function output.
596600
}
597601

llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ static const std::map<std::string, SPIRV::Extension::Extension>
6868
SPIRV::Extension::Extension::SPV_KHR_shader_clock},
6969
{"SPV_KHR_cooperative_matrix",
7070
SPIRV::Extension::Extension::SPV_KHR_cooperative_matrix},
71-
};
71+
{"SPV_KHR_non_semantic_info",
72+
SPIRV::Extension::Extension::SPV_KHR_non_semantic_info}};
7273

7374
bool SPIRVExtensionsParser::parse(cl::Option &O, llvm::StringRef ArgName,
7475
llvm::StringRef ArgValue,
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
#include "MCTargetDesc/SPIRVBaseInfo.h"
2+
#include "MCTargetDesc/SPIRVMCTargetDesc.h"
3+
#include "SPIRVGlobalRegistry.h"
4+
#include "SPIRVRegisterInfo.h"
5+
#include "SPIRVTargetMachine.h"
6+
#include "llvm/ADT/SmallString.h"
7+
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
8+
#include "llvm/CodeGen/MachineBasicBlock.h"
9+
#include "llvm/CodeGen/MachineFunction.h"
10+
#include "llvm/CodeGen/MachineFunctionPass.h"
11+
#include "llvm/CodeGen/MachineInstr.h"
12+
#include "llvm/CodeGen/MachineInstrBuilder.h"
13+
#include "llvm/CodeGen/MachineModuleInfo.h"
14+
#include "llvm/CodeGen/MachineOperand.h"
15+
#include "llvm/IR/DebugInfoMetadata.h"
16+
#include "llvm/IR/Metadata.h"
17+
#include "llvm/PassRegistry.h"
18+
#include "llvm/Support/Casting.h"
19+
#include "llvm/Support/Path.h"
20+
21+
#define DEBUG_TYPE "spirv-nonsemantic-debug-info"
22+
23+
namespace llvm {
24+
struct SPIRVEmitNonSemanticDI : public MachineFunctionPass {
25+
static char ID;
26+
SPIRVTargetMachine *TM;
27+
SPIRVEmitNonSemanticDI(SPIRVTargetMachine *TM);
28+
SPIRVEmitNonSemanticDI();
29+
30+
bool runOnMachineFunction(MachineFunction &MF) override;
31+
32+
private:
33+
bool IsGlobalDIEmitted = false;
34+
bool emitGlobalDI(MachineFunction &MF);
35+
};
36+
37+
void initializeSPIRVEmitNonSemanticDIPass(PassRegistry &);
38+
39+
FunctionPass *createSPIRVEmitNonSemanticDIPass(SPIRVTargetMachine *TM) {
40+
return new SPIRVEmitNonSemanticDI(TM);
41+
}
42+
} // namespace llvm
43+
44+
using namespace llvm;
45+
46+
INITIALIZE_PASS(SPIRVEmitNonSemanticDI, DEBUG_TYPE,
47+
"SPIRV NonSemantic.Shader.DebugInfo.100 emitter", false, false)
48+
49+
char SPIRVEmitNonSemanticDI::ID = 0;
50+
51+
SPIRVEmitNonSemanticDI::SPIRVEmitNonSemanticDI(SPIRVTargetMachine *TM)
52+
: MachineFunctionPass(ID), TM(TM) {
53+
initializeSPIRVEmitNonSemanticDIPass(*PassRegistry::getPassRegistry());
54+
}
55+
56+
SPIRVEmitNonSemanticDI::SPIRVEmitNonSemanticDI() : MachineFunctionPass(ID) {
57+
initializeSPIRVEmitNonSemanticDIPass(*PassRegistry::getPassRegistry());
58+
}
59+
60+
bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF) {
61+
// If this MachineFunction doesn't have any BB repeat procedure
62+
// for the next
63+
if (MF.begin() == MF.end()) {
64+
IsGlobalDIEmitted = false;
65+
return false;
66+
}
67+
68+
// Required variables to get from metadata search
69+
LLVMContext *Context;
70+
SmallString<128> FilePath;
71+
unsigned SourceLanguage = 0;
72+
int64_t DwarfVersion = 0;
73+
int64_t DebugInfoVersion = 0;
74+
75+
// Searching through the Module metadata to find nescessary
76+
// information like DwarfVersion or SourceLanguage
77+
{
78+
const MachineModuleInfo &MMI =
79+
getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
80+
const Module *M = MMI.getModule();
81+
Context = &M->getContext();
82+
const NamedMDNode *DbgCu = M->getNamedMetadata("llvm.dbg.cu");
83+
if (!DbgCu)
84+
return false;
85+
for (const auto *Op : DbgCu->operands()) {
86+
if (const auto *CompileUnit = dyn_cast<DICompileUnit>(Op)) {
87+
DIFile *File = CompileUnit->getFile();
88+
sys::path::append(FilePath, File->getDirectory(), File->getFilename());
89+
SourceLanguage = CompileUnit->getSourceLanguage();
90+
break;
91+
}
92+
}
93+
const NamedMDNode *ModuleFlags = M->getNamedMetadata("llvm.module.flags");
94+
for (const auto *Op : ModuleFlags->operands()) {
95+
const MDOperand &MaybeStrOp = Op->getOperand(1);
96+
if (MaybeStrOp.equalsStr("Dwarf Version"))
97+
DwarfVersion =
98+
cast<ConstantInt>(
99+
cast<ConstantAsMetadata>(Op->getOperand(2))->getValue())
100+
->getSExtValue();
101+
else if (MaybeStrOp.equalsStr("Debug Info Version"))
102+
DebugInfoVersion =
103+
cast<ConstantInt>(
104+
cast<ConstantAsMetadata>(Op->getOperand(2))->getValue())
105+
->getSExtValue();
106+
}
107+
}
108+
// NonSemantic.Shader.DebugInfo.100 global DI instruction emitting
109+
{
110+
// Required LLVM variables for emitting logic
111+
const SPIRVInstrInfo *TII = TM->getSubtargetImpl()->getInstrInfo();
112+
const SPIRVRegisterInfo *TRI = TM->getSubtargetImpl()->getRegisterInfo();
113+
const RegisterBankInfo *RBI = TM->getSubtargetImpl()->getRegBankInfo();
114+
SPIRVGlobalRegistry *GR = TM->getSubtargetImpl()->getSPIRVGlobalRegistry();
115+
MachineRegisterInfo &MRI = MF.getRegInfo();
116+
MachineBasicBlock &MBB = *MF.begin();
117+
118+
// To correct placement of a OpLabel instruction during SPIRVAsmPrinter
119+
// emission all new instructions needs to be placed after OpFunction
120+
// and before first terminator
121+
MachineIRBuilder MIRBuilder(MBB, MBB.getFirstTerminator());
122+
123+
// Emit OpString with FilePath which is required by DebugSource
124+
const Register StrReg = MRI.createVirtualRegister(&SPIRV::IDRegClass);
125+
MRI.setType(StrReg, LLT::scalar(32));
126+
MachineInstrBuilder MIB = MIRBuilder.buildInstr(SPIRV::OpString);
127+
MIB.addDef(StrReg);
128+
addStringImm(FilePath, MIB);
129+
130+
const SPIRVType *VoidTy =
131+
GR->getOrCreateSPIRVType(Type::getVoidTy(*Context), MIRBuilder);
132+
133+
// Emit DebugSource which is required by DebugCompilationUnit
134+
const Register DebugSourceResIdReg =
135+
MRI.createVirtualRegister(&SPIRV::IDRegClass);
136+
MRI.setType(DebugSourceResIdReg, LLT::scalar(32));
137+
MIB = MIRBuilder.buildInstr(SPIRV::OpExtInst)
138+
.addDef(DebugSourceResIdReg)
139+
.addUse(GR->getSPIRVTypeID(VoidTy))
140+
.addImm(static_cast<int64_t>(
141+
SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100))
142+
.addImm(SPIRV::NonSemanticExtInst::DebugSource)
143+
.addUse(StrReg);
144+
MIB.constrainAllUses(*TII, *TRI, *RBI);
145+
GR->assignSPIRVTypeToVReg(VoidTy, DebugSourceResIdReg, MF);
146+
147+
const SPIRVType *I32Ty =
148+
GR->getOrCreateSPIRVType(Type::getInt32Ty(*Context), MIRBuilder);
149+
150+
// Convert DwarfVersion, DebugInfo and SourceLanguage integers to OpConstant
151+
// instructions required by DebugCompilationUnit
152+
const Register DwarfVersionReg =
153+
GR->buildConstantInt(DwarfVersion, MIRBuilder, I32Ty, false);
154+
const Register DebugInfoVersionReg =
155+
GR->buildConstantInt(DebugInfoVersion, MIRBuilder, I32Ty, false);
156+
const Register SourceLanguageReg =
157+
GR->buildConstantInt(SourceLanguage, MIRBuilder, I32Ty, false);
158+
159+
// Emit DebugCompilationUnit
160+
const Register DebugCompUnitResIdReg =
161+
MRI.createVirtualRegister(&SPIRV::IDRegClass);
162+
MRI.setType(DebugCompUnitResIdReg, LLT::scalar(32));
163+
MIB = MIRBuilder.buildInstr(SPIRV::OpExtInst)
164+
.addDef(DebugCompUnitResIdReg)
165+
.addUse(GR->getSPIRVTypeID(VoidTy))
166+
.addImm(static_cast<int64_t>(
167+
SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100))
168+
.addImm(SPIRV::NonSemanticExtInst::DebugCompilationUnit)
169+
.addUse(DebugInfoVersionReg)
170+
.addUse(DwarfVersionReg)
171+
.addUse(DebugSourceResIdReg)
172+
.addUse(SourceLanguageReg);
173+
MIB.constrainAllUses(*TII, *TRI, *RBI);
174+
GR->assignSPIRVTypeToVReg(VoidTy, DebugCompUnitResIdReg, MF);
175+
}
176+
return true;
177+
}
178+
179+
bool SPIRVEmitNonSemanticDI::runOnMachineFunction(MachineFunction &MF) {
180+
bool Res = false;
181+
// emitGlobalDI needs to be executed only once to avoid
182+
// emitting duplicates
183+
if (!IsGlobalDIEmitted) {
184+
IsGlobalDIEmitted = true;
185+
Res = emitGlobalDI(MF);
186+
}
187+
return Res;
188+
}

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
#include "SPIRVSubtarget.h"
2222
#include "SPIRVTargetMachine.h"
2323
#include "SPIRVUtils.h"
24-
#include "TargetInfo/SPIRVTargetInfo.h"
2524
#include "llvm/ADT/STLExtras.h"
2625
#include "llvm/CodeGen/MachineModuleInfo.h"
2726
#include "llvm/CodeGen/TargetPassConfig.h"
@@ -427,7 +426,19 @@ void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
427426
if (MAI.getSkipEmission(&MI))
428427
continue;
429428
const unsigned OpCode = MI.getOpcode();
430-
if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) {
429+
if (OpCode == SPIRV::OpString) {
430+
collectOtherInstr(MI, MAI, SPIRV::MB_DebugStrings, IS);
431+
} else if (OpCode == SPIRV::OpExtInst) {
432+
MachineOperand Ins = MI.getOperand(3);
433+
namespace NS = SPIRV::NonSemanticExtInst;
434+
static constexpr int64_t GlobalNonSemanticDITy[] = {
435+
NS::DebugSource, NS::DebugCompilationUnit};
436+
bool IsGlobalDI = false;
437+
for (unsigned Idx = 0; Idx < std::size(GlobalNonSemanticDITy); ++Idx)
438+
IsGlobalDI |= Ins.getImm() == GlobalNonSemanticDITy[Idx];
439+
if (IsGlobalDI)
440+
collectOtherInstr(MI, MAI, SPIRV::MB_NonSemanticGlobalDI, IS);
441+
} else if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) {
431442
collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames, IS);
432443
} else if (OpCode == SPIRV::OpEntryPoint) {
433444
collectOtherInstr(MI, MAI, SPIRV::MB_EntryPoints, IS);
@@ -899,6 +910,14 @@ void addInstrRequirements(const MachineInstr &MI,
899910
Reqs.addCapability(SPIRV::Capability::Float16Buffer);
900911
break;
901912
}
913+
case SPIRV::OpExtInst: {
914+
if (MI.getOperand(2).getImm() ==
915+
static_cast<int64_t>(
916+
SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100)) {
917+
Reqs.addExtension(SPIRV::Extension::SPV_KHR_non_semantic_info);
918+
}
919+
break;
920+
}
902921
case SPIRV::OpBitReverse:
903922
case SPIRV::OpBitFieldInsert:
904923
case SPIRV::OpBitFieldSExtract:

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#include "llvm/ADT/DenseMap.h"
2121
#include "llvm/ADT/SmallSet.h"
2222
#include "llvm/ADT/SmallVector.h"
23-
#include "llvm/ADT/StringMap.h"
2423

2524
namespace llvm {
2625
class SPIRVSubtarget;
@@ -34,9 +33,11 @@ enum ModuleSectionType {
3433
MB_EntryPoints, // All OpEntryPoint instructions (if any).
3534
// MB_ExecutionModes, MB_DebugSourceAndStrings,
3635
MB_DebugNames, // All OpName and OpMemberName intrs.
36+
MB_DebugStrings, // All OpString intrs.
3737
MB_DebugModuleProcessed, // All OpModuleProcessed instructions.
3838
MB_Annotations, // OpDecorate, OpMemberDecorate etc.
3939
MB_TypeConstVars, // OpTypeXXX, OpConstantXXX, and global OpVariables.
40+
MB_NonSemanticGlobalDI, // OpExtInst with e.g. DebugSource, DebugTypeBasic.
4041
MB_ExtFuncDecls, // OpFunction etc. to declare for external funcs.
4142
NUM_MODULE_SECTIONS // Total number of sections requiring basic blocks.
4243
};

llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ class SPIRVPassConfig : public TargetPassConfig {
115115
void addOptimizedRegAlloc() override {}
116116

117117
void addPostRegAlloc() override;
118+
void addPreEmitPass() override;
118119

119120
private:
120121
const SPIRVTargetMachine &TM;
@@ -208,6 +209,17 @@ bool SPIRVPassConfig::addRegBankSelect() {
208209
return false;
209210
}
210211

212+
static cl::opt<bool> SPVEnableNonSemanticDI(
213+
"spv-emit-nonsemantic-debug-info",
214+
cl::desc("Emit SPIR-V NonSemantic.Shader.DebugInfo.100 instructions"),
215+
cl::Optional, cl::init(false));
216+
217+
void SPIRVPassConfig::addPreEmitPass() {
218+
if (SPVEnableNonSemanticDI) {
219+
addPass(createSPIRVEmitNonSemanticDIPass(&getTM<SPIRVTargetMachine>()));
220+
}
221+
}
222+
211223
namespace {
212224
// A custom subclass of InstructionSelect, which is mostly the same except from
213225
// not requiring RegBankSelect to occur previously.

0 commit comments

Comments
 (0)