Skip to content

Commit

Permalink
Add Shader.DebugInfo.100 support for DebugSourceContinued (#4030)
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-lunarg authored Oct 27, 2021
1 parent 53ccb3a commit 4652250
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 5 deletions.
9 changes: 9 additions & 0 deletions include/dxc/Support/SPIRVOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ struct SpirvCodeGenOptions {
bool reduceLoadSize;
bool autoShiftBindings;
bool supportNonzeroBaseInstance;
/// Maximum length in words for the OpString literal containing the shader
/// source for DebugSource and DebugSourceContinued. If the source code length
/// is larger than this number, we will use DebugSourceContinued instructions
/// for follow-up source code after the first DebugSource instruction. Note
/// that this number must be less than or equal to 0xFFFDu because of the
/// limitation of a single SPIR-V instruction size (0xFFFF) - 2 operand words
/// for OpString. Currently a smaller value is only used to test
/// DebugSourceContinued generation.
uint32_t debugSourceLen;
SpirvLayoutRule cBufferLayoutRule;
SpirvLayoutRule sBufferLayoutRule;
SpirvLayoutRule tBufferLayoutRule;
Expand Down
17 changes: 17 additions & 0 deletions lib/DxcSupport/HLSLOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,14 @@ static bool handleVkShiftArgs(const InputArgList &args, OptSpecifier id,
}
return true;
}

namespace {

/// Maximum size of OpString instruction minus two operands
static const uint32_t kDefaultMaximumSourceLength = 0xFFFDu;
static const uint32_t kTestingMaximumSourceLength = 13u;

}
#endif
// SPIRV Change Ends

Expand Down Expand Up @@ -937,6 +945,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
opts.SpirvOptions.debugInfoLine = opts.SpirvOptions.debugInfoTool = false;
opts.SpirvOptions.debugInfoRich = false;
opts.SpirvOptions.debugInfoVulkan = false;
opts.SpirvOptions.debugSourceLen = kDefaultMaximumSourceLength;
if (Args.hasArg(OPT_fspv_debug_EQ)) {
opts.DebugInfo = true;
for (const Arg *A : Args.filtered(OPT_fspv_debug_EQ)) {
Expand Down Expand Up @@ -974,6 +983,14 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
opts.SpirvOptions.debugInfoLine = true;
opts.SpirvOptions.debugInfoRich = true;
opts.SpirvOptions.debugInfoVulkan = true;
} else if (v == "vulkan-with-source-test") {
// For test purposes only
opts.SpirvOptions.debugInfoFile = true;
opts.SpirvOptions.debugInfoSource = true;
opts.SpirvOptions.debugInfoLine = true;
opts.SpirvOptions.debugInfoRich = true;
opts.SpirvOptions.debugInfoVulkan = true;
opts.SpirvOptions.debugSourceLen = kTestingMaximumSourceLength;
} else {
errors << "unknown SPIR-V debug info control parameter: " << v;
return 1;
Expand Down
84 changes: 79 additions & 5 deletions tools/clang/lib/SPIRV/EmitVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@

namespace {

static const uint32_t kMaximumCharOpSource = 0xFFFA;
static const uint32_t kMaximumCharOpSourceContinued = 0xFFFD;

/// Chops the given original string into multiple smaller ones to make sure they
/// can be encoded in a sequence of OpSourceContinued instructions following an
/// OpSource instruction.
void chopString(llvm::StringRef original,
llvm::SmallVectorImpl<llvm::StringRef> *chopped) {
const uint32_t maxCharInOpSource = 0xFFFFu - 5u; // Minus operands and nul
const uint32_t maxCharInContinue = 0xFFFFu - 2u; // Minus opcode and nul

llvm::SmallVectorImpl<llvm::StringRef> *chopped,
uint32_t maxCharInOpSource, uint32_t maxCharInContinue) {
chopped->clear();
if (original.size() > maxCharInOpSource) {
chopped->push_back(llvm::StringRef(original.data(), maxCharInOpSource));
Expand Down Expand Up @@ -552,7 +553,8 @@ bool EmitVisitor::visit(SpirvSource *inst) {
if (spvOptions.debugInfoSource && inst->hasFile()) {
text = ReadSourceCode(inst->getFile()->getString());
if (!text.empty()) {
chopString(text, &choppedSrcCode);
chopString(text, &choppedSrcCode, kMaximumCharOpSource,
kMaximumCharOpSourceContinued);
if (!choppedSrcCode.empty()) {
firstSnippet = llvm::Optional<llvm::StringRef>(choppedSrcCode.front());
}
Expand Down Expand Up @@ -1262,10 +1264,82 @@ bool EmitVisitor::visit(SpirvDebugInfoNone *inst) {
return true;
}

void EmitVisitor::generateDebugSource(uint32_t fileId, uint32_t textId,
SpirvDebugSource *inst) {
initInstruction(inst);
curInst.push_back(inst->getResultTypeId());
curInst.push_back(getOrAssignResultId<SpirvInstruction>(inst));
curInst.push_back(
getOrAssignResultId<SpirvInstruction>(inst->getInstructionSet()));
curInst.push_back(inst->getDebugOpcode());
curInst.push_back(fileId);
if (textId)
curInst.push_back(textId);
finalizeInstruction(&richDebugInfo);
}

void EmitVisitor::generateDebugSourceContinued(uint32_t textId,
SpirvDebugSource *inst) {
initInstruction(spv::Op::OpExtInst, /* SourceLocation */ {});
curInst.push_back(inst->getResultTypeId());
curInst.push_back(takeNextId());
curInst.push_back(
getOrAssignResultId<SpirvInstruction>(inst->getInstructionSet()));
curInst.push_back(102u); // DebugSourceContinued
curInst.push_back(textId);
finalizeInstruction(&richDebugInfo);
}

void EmitVisitor::generateChoppedSource(uint32_t fileId, SpirvDebugSource *inst) {
// Chop up the source into multiple segments if it is too long.
llvm::Optional<llvm::StringRef> firstSnippet = llvm::None;
llvm::SmallVector<llvm::StringRef, 2> choppedSrcCode;
std::string text;
uint32_t textId = 0;
if (spvOptions.debugInfoSource) {
text = ReadSourceCode(inst->getFile());
if (!text.empty()) {
// Maximum characters for DebugSource and DebugSourceContinued
// OpString literal minus terminating null.
uint32_t maxChar = spvOptions.debugSourceLen * sizeof(uint32_t) - 1;
chopString(text, &choppedSrcCode, maxChar, maxChar);
if (!choppedSrcCode.empty()) {
firstSnippet = llvm::Optional<llvm::StringRef>(choppedSrcCode.front());
}
}
if (firstSnippet.hasValue())
textId = getOrCreateOpStringId(firstSnippet.getValue());
}
// Generate DebugSource
generateDebugSource(fileId, textId, inst);

// Now emit DebugSourceContinued for the [second:last] snippets.
for (uint32_t i = 1; i < choppedSrcCode.size(); ++i) {
textId = getOrCreateOpStringId(choppedSrcCode[i]);
generateDebugSourceContinued(textId, inst);
}
}

bool EmitVisitor::visit(SpirvDebugSource *inst) {
// Emit the OpString for the file name.
uint32_t fileId = getOrCreateOpStringId(inst->getFile());
if (!debugMainFileId)
debugMainFileId = fileId;

if (dumpedFiles.count(fileId) != 0)
return true;
dumpedFiles.insert(fileId);

if (spvOptions.debugInfoVulkan) {
generateChoppedSource(fileId, inst);
return true;
}
// OpenCL.DebugInfo.100
// TODO(greg-lunarg): This logic does not currently handle text that is too
// long for a string. In this case, the entire compiler returns without
// producing a SPIR-V file. Once DebugSourceContinued is added to
// OpenCL.DebugInfo.100, the logic below can be removed and the
// NonSemantic.Shader.DebugInfo.100 logic above can be used for both cases.
uint32_t textId = 0;
if (spvOptions.debugInfoSource) {
auto text = ReadSourceCode(inst->getFile());
Expand Down
11 changes: 11 additions & 0 deletions tools/clang/lib/SPIRV/EmitVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,17 @@ class EmitVisitor : public Visitor {
/// one. Otherwise, create it, keep it in stringIdMap, and return its id.
uint32_t getOrCreateOpStringId(llvm::StringRef str);

// Generate DebugSource for inst
void generateDebugSource(uint32_t fileId, uint32_t textId,
SpirvDebugSource *inst);

// Generate DebugSourceContinued for inst
void generateDebugSourceContinued(uint32_t textId, SpirvDebugSource *inst);

/// Generate DebugSource and DebugSourceContinue for inst using previously
/// generated fileId, chopping source into pieces as needed.
void generateChoppedSource(uint32_t fileId, SpirvDebugSource *inst);

/// In the OpenCL.DebugInfo.100 spec some parameters are literals, where in
/// the NonSemantic.Shader.DebugInfo.100 spec they are encoded as constant
/// operands. This function takes care of checking which version we are
Expand Down
27 changes: 27 additions & 0 deletions tools/clang/test/CodeGenSPIRV/shader.debug.source.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Run: %dxc -T ps_6_0 -E MainPs -fspv-debug=vulkan-with-source

// CHECK: DebugSource {{%\d+}} {{%\d+}}
// CHECK-NOT: DebugSourceContinued

Texture2D g_tColor;

SamplerState g_sAniso;

struct PS_INPUT
{
float2 vTextureCoords : TEXCOORD2 ;
} ;

struct PS_OUTPUT
{
float4 vColor : SV_Target0 ;
} ;

PS_OUTPUT MainPs ( PS_INPUT i )
{
PS_OUTPUT ps_output ;

ps_output . vColor = g_tColor . Sample ( g_sAniso , i . vTextureCoords . xy ) ;
return ps_output ;
}

27 changes: 27 additions & 0 deletions tools/clang/test/CodeGenSPIRV/shader.debug.sourcecontinued.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Run: %dxc -T ps_6_0 -E MainPs -fspv-debug=vulkan-with-source-test

// CHECK: DebugSource {{%\d+}} {{%\d+}}
// CHECK: DebugSourceContinued {{%\d+}}

Texture2D g_tColor;

SamplerState g_sAniso;

struct PS_INPUT
{
float2 vTextureCoords : TEXCOORD2 ;
} ;

struct PS_OUTPUT
{
float4 vColor : SV_Target0 ;
} ;

PS_OUTPUT MainPs ( PS_INPUT i )
{
PS_OUTPUT ps_output ;

ps_output . vColor = g_tColor . Sample ( g_sAniso , i . vTextureCoords . xy ) ;
return ps_output ;
}

6 changes: 6 additions & 0 deletions tools/clang/unittests/SPIRV/CodeGenSpirvTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2960,5 +2960,11 @@ TEST_F(FileTest, ShaderDebugInfoFunction) {
TEST_F(FileTest, ShaderDebugInfoDebugLexicalBlock) {
runFileTest("shader.debug.debuglexicalblock.hlsl");
}
TEST_F(FileTest, ShaderDebugInfoSource) {
runFileTest("shader.debug.source.hlsl");
}
TEST_F(FileTest, ShaderDebugInfoSourceContinued) {
runFileTest("shader.debug.sourcecontinued.hlsl");
}

} // namespace

0 comments on commit 4652250

Please sign in to comment.