Skip to content

Commit 7f985ff

Browse files
authored
[spirv] generate OpenCL.DebugInfo.100 instructions (#3155)
OpenCL.DebugInfo.100 is a SPIR-V extended instruction set that provides DWARF style debug information. This PR allows DXC SPIR-V backend to generate the rich HLSL debug information using OpenCL.DebugInfo.100 instructions.
1 parent 5f8ab16 commit 7f985ff

74 files changed

Lines changed: 4670 additions & 454 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

external/SPIRV-Tools

Submodule SPIRV-Tools updated 165 files

include/dxc/Support/SPIRVOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct SpirvCodeGenOptions {
4242
bool debugInfoLine;
4343
bool debugInfoSource;
4444
bool debugInfoTool;
45+
bool debugInfoRich;
4546
bool defaultRowMajor;
4647
bool disableValidation;
4748
bool enable16BitTypes;

lib/DxcSupport/HLSLOptions.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
817817

818818
opts.SpirvOptions.debugInfoFile = opts.SpirvOptions.debugInfoSource = false;
819819
opts.SpirvOptions.debugInfoLine = opts.SpirvOptions.debugInfoTool = false;
820+
opts.SpirvOptions.debugInfoRich = false;
820821
if (Args.hasArg(OPT_fspv_debug_EQ)) {
821822
opts.DebugInfo = true;
822823
for (const Arg *A : Args.filtered(OPT_fspv_debug_EQ)) {
@@ -832,6 +833,16 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
832833
opts.SpirvOptions.debugInfoLine = true;
833834
} else if (v == "tool") {
834835
opts.SpirvOptions.debugInfoTool = true;
836+
} else if (v == "rich") {
837+
opts.SpirvOptions.debugInfoFile = true;
838+
opts.SpirvOptions.debugInfoSource = false;
839+
opts.SpirvOptions.debugInfoLine = true;
840+
opts.SpirvOptions.debugInfoRich = true;
841+
} else if (v == "rich-with-source") {
842+
opts.SpirvOptions.debugInfoFile = true;
843+
opts.SpirvOptions.debugInfoSource = true;
844+
opts.SpirvOptions.debugInfoLine = true;
845+
opts.SpirvOptions.debugInfoRich = true;
835846
} else {
836847
errors << "unknown SPIR-V debug info control parameter: " << v;
837848
return 1;

tools/clang/include/clang/SPIRV/SpirvBasicBlock.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,29 @@ class SpirvBasicBlock {
7070
/// OpLoopMerge instruction. Returns nullptr otherwise.
7171
SpirvBasicBlock *getContinueTarget() const { return continueTarget; }
7272

73+
/// Get/set DebugScope for this basic block.
74+
SpirvDebugScope *getDebugScope() const { return debugScope; }
75+
void setDebugScope(SpirvDebugScope *scope) {
76+
assert(debugScope == nullptr);
77+
debugScope = scope;
78+
}
79+
80+
/// Deletes existing debugScope and sets scope as the new debugScope.
81+
void updateDebugScope(SpirvDebugScope *scope) {
82+
if (debugScope != nullptr) {
83+
debugScope->releaseMemory();
84+
debugScope = nullptr;
85+
}
86+
setDebugScope(scope);
87+
}
88+
7389
/// Adds an instruction to the vector of instructions belonging to this basic
7490
/// block.
7591
void addInstruction(SpirvInstruction *inst) { instructions.push_back(inst); }
7692

93+
/// Return true if instructions is empty. Otherwise, return false.
94+
bool empty() { return instructions.empty(); }
95+
7796
/// Returns true if the last instruction in this basic block is a termination
7897
/// instruction.
7998
bool hasTerminator() const;
@@ -106,6 +125,13 @@ class SpirvBasicBlock {
106125
/// The continue merge targets if this basic block is a header block
107126
/// of structured control flow.
108127
SpirvBasicBlock *continueTarget;
128+
129+
/// DebugScope that groups all instructions in this basic block.
130+
/// TODO: There can be multiple DebugScope instructions in a basic block.
131+
/// Currently, we do not find an actual case that DXC has to emit
132+
/// multiple DebugScope instructions in a basic block, but update it
133+
/// when we find the actual case.
134+
SpirvDebugScope *debugScope;
109135
};
110136

111137
} // end namespace spirv

tools/clang/include/clang/SPIRV/SpirvBuilder.h

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ class SpirvBuilder {
103103
/// for the basic block. On failure, returns zero.
104104
SpirvBasicBlock *createBasicBlock(llvm::StringRef name = "");
105105

106+
/// \brief Creates a SPIR-V DebugScope (OpenCL.DebugInfo.100 instruction).
107+
/// On success, returns the <id> of DebugScope. On failure, returns nullptr.
108+
SpirvDebugScope *createDebugScope(SpirvDebugInstruction *scope);
109+
106110
/// \brief Adds the basic block with the given label as a successor to the
107111
/// current basic block.
108112
void addSuccessor(SpirvBasicBlock *successorBB);
@@ -126,6 +130,9 @@ class SpirvBuilder {
126130
/// \brief Sets insertion point to the given basic block.
127131
inline void setInsertPoint(SpirvBasicBlock *bb) { insertPoint = bb; }
128132

133+
/// \brief Gets insertion point.
134+
inline SpirvBasicBlock *getInsertPoint() { return insertPoint; }
135+
129136
// === Instruction at the current Insertion Point ===
130137

131138
/// \brief Creates a composite construct instruction with the given
@@ -445,6 +452,44 @@ class SpirvBuilder {
445452
/// \brief Creates an OpDemoteToHelperInvocationEXT instruction.
446453
SpirvInstruction *createDemoteToHelperInvocationEXT(SourceLocation);
447454

455+
// === SPIR-V Rich Debug Info Creation ===
456+
SpirvDebugSource *createDebugSource(llvm::StringRef file,
457+
llvm::StringRef text = "");
458+
459+
SpirvDebugCompilationUnit *createDebugCompilationUnit(SpirvDebugSource *);
460+
461+
SpirvDebugLexicalBlock *
462+
createDebugLexicalBlock(SpirvDebugSource *, uint32_t line, uint32_t column,
463+
SpirvDebugInstruction *parent);
464+
465+
SpirvDebugLocalVariable *createDebugLocalVariable(
466+
QualType debugType, llvm::StringRef varName, SpirvDebugSource *src,
467+
uint32_t line, uint32_t column, SpirvDebugInstruction *parentScope,
468+
uint32_t flags, llvm::Optional<uint32_t> argNumber = llvm::None);
469+
470+
SpirvDebugGlobalVariable *createDebugGlobalVariable(
471+
QualType debugType, llvm::StringRef varName, SpirvDebugSource *src,
472+
uint32_t line, uint32_t column, SpirvDebugInstruction *parentScope,
473+
llvm::StringRef linkageName, SpirvVariable *var, uint32_t flags,
474+
llvm::Optional<SpirvInstruction *> staticMemberDebugType = llvm::None);
475+
476+
// Get a DebugInfoNone if exists. Otherwise, create one and return it.
477+
SpirvDebugInfoNone *getOrCreateDebugInfoNone();
478+
479+
// Get a null DebugExpression if exists. Otherwise, create one and return it.
480+
SpirvDebugExpression *getOrCreateNullDebugExpression();
481+
482+
SpirvDebugDeclare *createDebugDeclare(
483+
SpirvDebugLocalVariable *dbgVar, SpirvInstruction *var,
484+
llvm::Optional<SpirvDebugExpression *> dbgExpr = llvm::None);
485+
486+
SpirvDebugFunction *
487+
createDebugFunction(const FunctionDecl *decl, llvm::StringRef name,
488+
SpirvDebugSource *src, uint32_t fnLine, uint32_t fnColumn,
489+
SpirvDebugInstruction *parentScope,
490+
llvm::StringRef linkageName, uint32_t flags,
491+
uint32_t scopeLine, SpirvFunction *fn);
492+
448493
/// \brief Create SPIR-V instructions for KHR RayQuery ops
449494
SpirvInstruction *
450495
createRayQueryOpsKHR(spv::Op opcode, QualType resultType,
@@ -464,7 +509,7 @@ class SpirvBuilder {
464509
/// content. Returns the SpirvString instruction of the file name.
465510
inline SpirvString *setDebugSource(uint32_t major, uint32_t minor,
466511
const std::vector<llvm::StringRef> &name,
467-
llvm::StringRef content);
512+
llvm::StringRef content = "");
468513

469514
/// \brief Adds an execution mode to the module under construction.
470515
inline void addExecutionMode(SpirvFunction *entryPoint, spv::ExecutionMode em,
@@ -474,6 +519,11 @@ class SpirvBuilder {
474519
/// construction.
475520
void addModuleProcessed(llvm::StringRef process);
476521

522+
/// \brief If not added already, adds an OpExtInstImport (import of extended
523+
/// instruction set) of the OpenCL.DebugInfo.100 instruction set. Returns the
524+
/// imported instruction set.
525+
SpirvExtInstImport *getOpenCLDebugInfoExtInstSet();
526+
477527
/// \brief Adds a stage input/ouput variable whose value is of the given type.
478528
///
479529
/// Note: the corresponding pointer type of the given type will not be
@@ -627,6 +677,11 @@ class SpirvBuilder {
627677
/// Used as caches for all created builtin variables to avoid duplication.
628678
llvm::SmallVector<BuiltInVarInfo, 16> builtinVars;
629679

680+
SpirvDebugInfoNone *debugNone;
681+
682+
/// DebugExpression that does not reference any DebugOperation
683+
SpirvDebugExpression *nullDebugExpr;
684+
630685
// To avoid generating multiple OpStrings for the same string literal
631686
// the SpirvBuilder will generate and reuse them.
632687
llvm::DenseMap<std::string, SpirvString *, StringMapInfo> stringLiterals;
@@ -667,7 +722,7 @@ SpirvBuilder::setDebugSource(uint32_t major, uint32_t minor,
667722
SpirvSource *debugSource = new (context)
668723
SpirvSource(/*SourceLocation*/ {}, spv::SourceLanguage::HLSL, version,
669724
fileString, content);
670-
mod->addDebugSource(debugSource);
725+
mod->addSource(debugSource);
671726
if (!mainSource)
672727
mainSource = debugSource;
673728
}
@@ -678,7 +733,7 @@ SpirvBuilder::setDebugSource(uint32_t major, uint32_t minor,
678733
mainSource = new (context)
679734
SpirvSource(/*SourceLocation*/ {}, spv::SourceLanguage::HLSL, version,
680735
nullptr, content);
681-
mod->addDebugSource(mainSource);
736+
mod->addSource(mainSource);
682737
}
683738
return mainSource->getFile();
684739
}

tools/clang/include/clang/SPIRV/SpirvContext.h

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,38 @@
1212
#include <array>
1313

1414
#include "dxc/DXIL/DxilShaderModel.h"
15+
#include "clang/AST/DeclTemplate.h"
1516
#include "clang/Frontend/FrontendAction.h"
1617
#include "clang/SPIRV/SpirvInstruction.h"
1718
#include "clang/SPIRV/SpirvType.h"
1819
#include "llvm/ADT/DenseMap.h"
1920
#include "llvm/ADT/DenseMapInfo.h"
21+
#include "llvm/ADT/MapVector.h"
22+
#include "llvm/ADT/StringMap.h"
2023
#include "llvm/Support/Allocator.h"
2124

2225
namespace clang {
2326
namespace spirv {
2427

28+
class SpirvModule;
29+
30+
struct RichDebugInfo {
31+
RichDebugInfo(SpirvDebugSource *src, SpirvDebugCompilationUnit *cu)
32+
: source(src), compilationUnit(cu) {
33+
scopeStack.push_back(cu);
34+
}
35+
RichDebugInfo() : source(nullptr), compilationUnit(nullptr), scopeStack() {}
36+
37+
// The HLL source code
38+
SpirvDebugSource *source;
39+
40+
// The compilation unit (topmost debug info node)
41+
SpirvDebugCompilationUnit *compilationUnit;
42+
43+
// Stack of lexical scopes
44+
std::vector<SpirvDebugInstruction *> scopeStack;
45+
};
46+
2547
// Provides DenseMapInfo for spv::StorageClass so that we can use
2648
// spv::StorageClass as key to DenseMap.
2749
//
@@ -139,10 +161,66 @@ class SpirvContext {
139161
/// Deallocates the memory pointed by the given pointer.
140162
void deallocate(void *ptr) const {}
141163

164+
// === DebugTypes ===
165+
166+
// TODO: Replace uint32_t with an enum for encoding.
167+
SpirvDebugType *getDebugTypeBasic(const SpirvType *spirvType,
168+
llvm::StringRef name, SpirvConstant *size,
169+
uint32_t encoding);
170+
171+
SpirvDebugType *getDebugTypeMember(llvm::StringRef name, SpirvDebugType *type,
172+
SpirvDebugSource *source, uint32_t line,
173+
uint32_t column,
174+
SpirvDebugInstruction *parent,
175+
uint32_t flags, uint32_t offsetInBits,
176+
uint32_t sizeInBits, const APValue *value);
177+
178+
SpirvDebugTypeComposite *getDebugTypeComposite(const SpirvType *spirvType,
179+
llvm::StringRef name,
180+
SpirvDebugSource *source,
181+
uint32_t line, uint32_t column,
182+
SpirvDebugInstruction *parent,
183+
llvm::StringRef linkageName,
184+
uint32_t flags, uint32_t tag);
185+
186+
SpirvDebugType *getDebugType(const SpirvType *spirvType);
187+
188+
SpirvDebugType *getDebugTypeArray(const SpirvType *spirvType,
189+
SpirvDebugInstruction *elemType,
190+
llvm::ArrayRef<uint32_t> elemCount);
191+
192+
SpirvDebugType *getDebugTypeVector(const SpirvType *spirvType,
193+
SpirvDebugInstruction *elemType,
194+
uint32_t elemCount);
195+
196+
SpirvDebugType *getDebugTypeFunction(const SpirvType *spirvType,
197+
uint32_t flags, SpirvDebugType *ret,
198+
llvm::ArrayRef<SpirvDebugType *> params);
199+
200+
SpirvDebugTypeTemplate *createDebugTypeTemplate(
201+
const ClassTemplateSpecializationDecl *templateType,
202+
SpirvDebugInstruction *target,
203+
const llvm::SmallVector<SpirvDebugTypeTemplateParameter *, 2> &params);
204+
205+
SpirvDebugTypeTemplate *
206+
getDebugTypeTemplate(const ClassTemplateSpecializationDecl *templateType);
207+
208+
SpirvDebugTypeTemplateParameter *createDebugTypeTemplateParameter(
209+
const TemplateArgument *templateArg, llvm::StringRef name,
210+
SpirvDebugType *type, SpirvInstruction *value, SpirvDebugSource *source,
211+
uint32_t line, uint32_t column);
212+
213+
SpirvDebugTypeTemplateParameter *
214+
getDebugTypeTemplateParameter(const TemplateArgument *templateArg);
215+
216+
// Moves all debug type instructions to module and makes the data structures
217+
// that contain the debug type instructions empty. After calling this method,
218+
// module will have the ownership of debug type instructions.
219+
void moveDebugTypesToModule(SpirvModule *module);
220+
142221
// === Types ===
143222

144223
const VoidType *getVoidType() const { return voidType; }
145-
146224
const BoolType *getBoolType() const { return boolType; }
147225
const IntegerType *getSIntType(uint32_t bitwidth);
148226
const IntegerType *getUIntType(uint32_t bitwidth);
@@ -237,6 +315,47 @@ class SpirvContext {
237315
return curShaderModelKind == ShaderModelKind::Amplification;
238316
}
239317

318+
/// Function to get all RichDebugInfo (i.e., the current status of
319+
/// compilation units).
320+
llvm::StringMap<RichDebugInfo> &getDebugInfo() { return debugInfo; }
321+
322+
/// Function to let the lexical scope stack grow when it enters a
323+
/// new lexical scope.
324+
void pushDebugLexicalScope(RichDebugInfo *info, SpirvDebugInstruction *scope);
325+
326+
/// Function to pop the last element from the lexical scope stack.
327+
void popDebugLexicalScope(RichDebugInfo *info) {
328+
info->scopeStack.pop_back();
329+
currentLexicalScope = info->scopeStack.back();
330+
}
331+
332+
/// Function to get the last lexical scope that the SpirvEmitter
333+
/// class instance entered.
334+
SpirvDebugInstruction *getCurrentLexicalScope() {
335+
return currentLexicalScope;
336+
}
337+
338+
/// Function to add/get the mapping from a SPIR-V type to its Decl for
339+
/// a struct type.
340+
void registerStructDeclForSpirvType(const SpirvType *spvTy,
341+
const DeclContext *decl) {
342+
assert(spvTy != nullptr && decl != nullptr);
343+
spvStructTypeToDecl[spvTy] = decl;
344+
}
345+
const DeclContext *getStructDeclForSpirvType(const SpirvType *spvTy) {
346+
return spvStructTypeToDecl[spvTy];
347+
}
348+
349+
/// Function to add/get the mapping from a FunctionDecl to its DebugFunction.
350+
void registerDebugFunctionForDecl(const FunctionDecl *decl,
351+
SpirvDebugFunction *fn) {
352+
assert(decl != nullptr && fn != nullptr);
353+
declToDebugFunction[decl] = fn;
354+
}
355+
SpirvDebugFunction *getDebugFunctionForDecl(const FunctionDecl *decl) {
356+
return declToDebugFunction[decl];
357+
}
358+
240359
private:
241360
/// \brief The allocator used to create SPIR-V entity objects.
242361
///
@@ -294,6 +413,35 @@ class SpirvContext {
294413
// Major/Minor hlsl profile version.
295414
uint32_t majorVersion;
296415
uint32_t minorVersion;
416+
417+
/// File name to rich debug info map. When the main source file
418+
/// includes header files, we create an element of debugInfo for
419+
/// each file. RichDebugInfo includes DebugSource,
420+
/// DebugCompilationUnit and scopeStack which keeps lexical scopes
421+
/// recursively.
422+
llvm::StringMap<RichDebugInfo> debugInfo;
423+
SpirvDebugInstruction *currentLexicalScope;
424+
425+
// Mapping from SPIR-V type to debug type instruction.
426+
// The purpose is not to generate several DebugType* instructions for the same
427+
// type if the type is used for several variables.
428+
llvm::MapVector<const SpirvType *, SpirvDebugType *> debugTypes;
429+
430+
// Mapping from template decl to DebugTypeTemplate.
431+
llvm::MapVector<const ClassTemplateSpecializationDecl *,
432+
SpirvDebugTypeTemplate *>
433+
typeTemplates;
434+
435+
// Mapping from template parameter decl to DebugTypeTemplateParameter.
436+
llvm::MapVector<const TemplateArgument *, SpirvDebugTypeTemplateParameter *>
437+
typeTemplateParams;
438+
439+
// Mapping from SPIR-V type to Decl for a struct type.
440+
llvm::DenseMap<const SpirvType *, const DeclContext *> spvStructTypeToDecl;
441+
442+
// Mapping from FunctionDecl to SPIR-V debug function.
443+
llvm::DenseMap<const FunctionDecl *, SpirvDebugFunction *>
444+
declToDebugFunction;
297445
};
298446

299447
} // end namespace spirv

0 commit comments

Comments
 (0)