Skip to content

Commit d0ae2fb

Browse files
committed
IR: Add SPIR-V disassembly for embedded downstream IR dumps
When dumping IR that contains embedded downstream SPIR-V code (via EmbeddedDownstreamIR instructions), display the disassembled SPIR-V instead of just showing "<binary blob>". This CL also does: - Adds a new interface for disassembly and get result. - Modify export-library-generics.slang test test to check for the disassembled SPIR-V Fixes shader-slang#6513
1 parent 5673edf commit d0ae2fb

File tree

7 files changed

+161
-9
lines changed

7 files changed

+161
-9
lines changed

source/compiler-core/slang-downstream-compiler.h

+12
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,9 @@ class IDownstreamCompiler : public ICastable
340340
/// Disassemble and print to stdout
341341
virtual SLANG_NO_THROW SlangResult SLANG_MCALL
342342
disassemble(const uint32_t* contents, int contentsSize) = 0;
343+
/// Disassemble and return the result as a string
344+
virtual SLANG_NO_THROW SlangResult SLANG_MCALL
345+
disassembleWithResult(const uint32_t* contents, int contentsSize, String& outString) = 0;
343346

344347
/// True if underlying compiler uses file system to communicate source
345348
virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased() = 0;
@@ -398,6 +401,15 @@ class DownstreamCompilerBase : public ComBaseObject, public IDownstreamCompiler
398401
return SLANG_FAIL;
399402
}
400403

404+
virtual SLANG_NO_THROW SlangResult SLANG_MCALL
405+
disassembleWithResult(const uint32_t* contents, int contentsSize, String& outString) SLANG_OVERRIDE
406+
{
407+
SLANG_UNUSED(contents);
408+
SLANG_UNUSED(contentsSize);
409+
SLANG_UNUSED(outString);
410+
return SLANG_FAIL;
411+
}
412+
401413
DownstreamCompilerBase(const Desc& desc)
402414
: m_desc(desc)
403415
{

source/compiler-core/slang-glslang-compiler.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class GlslangDownstreamCompiler : public DownstreamCompilerBase
4949
validate(const uint32_t* contents, int contentsSize) SLANG_OVERRIDE;
5050
virtual SLANG_NO_THROW SlangResult SLANG_MCALL
5151
disassemble(const uint32_t* contents, int contentsSize) SLANG_OVERRIDE;
52+
virtual SLANG_NO_THROW SlangResult SLANG_MCALL
53+
disassembleWithResult(const uint32_t* contents, int contentsSize, String& outString) SLANG_OVERRIDE;
5254
int link(
5355
const uint32_t** modules,
5456
const uint32_t* moduleSizes,
@@ -71,6 +73,7 @@ class GlslangDownstreamCompiler : public DownstreamCompilerBase
7173
glslang_CompileFunc_1_2 m_compile_1_2 = nullptr;
7274
glslang_ValidateSPIRVFunc m_validate = nullptr;
7375
glslang_DisassembleSPIRVFunc m_disassemble = nullptr;
76+
glslang_DisassembleSPIRVWithResultFunc m_disassembleWithResult = nullptr;
7477
glslang_LinkSPIRVFunc m_link = nullptr;
7578

7679
ComPtr<ISlangSharedLibrary> m_sharedLibrary;
@@ -86,6 +89,8 @@ SlangResult GlslangDownstreamCompiler::init(ISlangSharedLibrary* library)
8689
m_validate = (glslang_ValidateSPIRVFunc)library->findFuncByName("glslang_validateSPIRV");
8790
m_disassemble =
8891
(glslang_DisassembleSPIRVFunc)library->findFuncByName("glslang_disassembleSPIRV");
92+
m_disassembleWithResult =
93+
(glslang_DisassembleSPIRVWithResultFunc)library->findFuncByName("glslang_disassembleSPIRVWithResult");
8994
m_link = (glslang_LinkSPIRVFunc)library->findFuncByName("glslang_linkSPIRV");
9095

9196
if (m_compile_1_0 == nullptr && m_compile_1_1 == nullptr && m_compile_1_2 == nullptr)
@@ -316,6 +321,25 @@ SlangResult GlslangDownstreamCompiler::validate(const uint32_t* contents, int co
316321
return SLANG_FAIL;
317322
}
318323

324+
SlangResult GlslangDownstreamCompiler::disassembleWithResult(const uint32_t* contents, int contentsSize, String& outString)
325+
{
326+
if (m_disassembleWithResult == nullptr)
327+
{
328+
return SLANG_FAIL;
329+
}
330+
331+
char* resultString = nullptr;
332+
if (m_disassembleWithResult(contents, contentsSize, &resultString))
333+
{
334+
if (resultString)
335+
{
336+
outString = String(resultString);
337+
return SLANG_OK;
338+
}
339+
}
340+
return SLANG_FAIL;
341+
}
342+
319343
SlangResult GlslangDownstreamCompiler::disassemble(const uint32_t* contents, int contentsSize)
320344
{
321345
if (m_disassemble == nullptr)

source/slang-glslang/slang-glslang.cpp

+33-8
Original file line numberDiff line numberDiff line change
@@ -184,36 +184,61 @@ extern "C"
184184
return tools.Validate(contents, contentsSize, options);
185185
}
186186

187-
// Disassemble the given SPIRV-ASM instructions.
187+
// Disassemble the given SPIRV-ASM instructions and return the result as a string.
188188
extern "C"
189189
#ifdef _MSC_VER
190-
_declspec(dllexport)
190+
_declspec(dllexport)
191191
#else
192-
__attribute__((__visibility__("default")))
192+
__attribute__((__visibility__("default")))
193193
#endif
194-
bool glslang_disassembleSPIRV(const uint32_t* contents, int contentsSize)
194+
bool glslang_disassembleSPIRVWithResult(const uint32_t * contents, int contentsSize, char** outString)
195195
{
196196
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
197+
spv_text text;
197198

198199
uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE;
199200
options |= SPV_BINARY_TO_TEXT_OPTION_COMMENT;
200-
options |= SPV_BINARY_TO_TEXT_OPTION_PRINT;
201-
options |= SPV_BINARY_TO_TEXT_OPTION_COLOR;
202201
options |= SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES;
202+
options |= SPV_BINARY_TO_TEXT_OPTION_INDENT;
203203

204204
spv_diagnostic diagnostic = nullptr;
205205
spv_context context = spvContextCreate(kDefaultEnvironment);
206206
spv_result_t error =
207-
spvBinaryToText(context, contents, contentsSize, options, nullptr, &diagnostic);
207+
spvBinaryToText(context, contents, contentsSize, options, &text, &diagnostic);
208208
spvContextDestroy(context);
209209
if (error)
210210
{
211211
spvDiagnosticPrint(diagnostic);
212212
spvDiagnosticDestroy(diagnostic);
213213
return false;
214214
}
215+
else
216+
{
217+
if (outString)
218+
{
219+
// Allocate memory for the output string and copy the result
220+
size_t len = text->length + 1; // +1 for null terminator
221+
*outString = new char[len];
222+
memcpy(*outString, text->str, text->length);
223+
(*outString)[text->length] = '\0'; // Ensure null termination
224+
}
225+
226+
spvTextDestroy(text);
227+
return true;
228+
}
229+
}
230+
215231

216-
return true;
232+
// Disassemble the given SPIRV-ASM instructions.
233+
extern "C"
234+
#ifdef _MSC_VER
235+
_declspec(dllexport)
236+
#else
237+
__attribute__((__visibility__("default")))
238+
#endif
239+
bool glslang_disassembleSPIRV(const uint32_t * contents, int contentsSize)
240+
{
241+
return glslang_disassembleSPIRVWithResult(contents, contentsSize, nullptr);
217242
}
218243

219244
// Apply the SPIRV-Tools optimizer to generated SPIR-V based on the desired optimization level

source/slang-glslang/slang-glslang.h

+1
Original file line numberDiff line numberDiff line change
@@ -166,5 +166,6 @@ typedef int (*glslang_CompileFunc_1_1)(glslang_CompileRequest_1_1* request);
166166
typedef int (*glslang_CompileFunc_1_2)(glslang_CompileRequest_1_2* request);
167167
typedef bool (*glslang_ValidateSPIRVFunc)(const uint32_t* contents, int contentsSize);
168168
typedef bool (*glslang_DisassembleSPIRVFunc)(const uint32_t* contents, int contentsSize);
169+
typedef bool (*glslang_DisassembleSPIRVWithResultFunc)(const uint32_t* contents, int contentsSize, char** outString);
169170
typedef bool (*glslang_LinkSPIRVFunc)(glslang_LinkRequest* request);
170171
#endif

source/slang/slang-ir.cpp

+69
Original file line numberDiff line numberDiff line change
@@ -7166,6 +7166,75 @@ static void dumpInstExpr(IRDumpContext* context, IRInst* inst)
71667166
}
71677167
}
71687168

7169+
// Special case EmbeddedDownstreamIR to show SPIR-V disassembly
7170+
if (op == kIROp_EmbeddedDownstreamIR)
7171+
{
7172+
auto targetInst = inst->getOperand(0);
7173+
auto blobInst = inst->getOperand(1);
7174+
7175+
// Get the target value
7176+
auto targetLit = as<IRIntLit>(targetInst);
7177+
if (!targetLit)
7178+
{
7179+
dump(context, "EmbeddedDownstreamIR(invalid target)");
7180+
return;
7181+
}
7182+
7183+
// Get the blob
7184+
auto blobLitInst = as<IRBlobLit>(blobInst);
7185+
if (!blobLitInst)
7186+
{
7187+
dump(context, "EmbeddedDownstreamIR(invalid blob)");
7188+
return;
7189+
}
7190+
7191+
dump(context, "EmbeddedDownstreamIR(");
7192+
dump(context, targetLit->getValue());
7193+
dump(context, " : Int, ");
7194+
7195+
// If target is SPIR-V (6), disassemble the blob
7196+
if (targetLit->getValue() == (IRIntegerValue)CodeGenTarget::SPIRV)
7197+
{
7198+
auto blob = blobLitInst->getStringSlice();
7199+
const uint32_t* spirvCode = (const uint32_t*)blob.begin();
7200+
const size_t spirvWordCount = blob.getLength() / sizeof(uint32_t);
7201+
7202+
// Get the compiler from the session through the module
7203+
auto module = inst->getModule();
7204+
auto session = module->getSession();
7205+
IDownstreamCompiler* compiler = session->getOrLoadDownstreamCompiler(
7206+
PassThroughMode::SpirvDis,
7207+
nullptr);
7208+
7209+
if (compiler)
7210+
{
7211+
// Use glslang interface to disassemble with string output
7212+
String disassemblyOutput;
7213+
if (SLANG_SUCCEEDED(compiler->disassembleWithResult(spirvCode, int(spirvWordCount), disassemblyOutput)))
7214+
{
7215+
// Dump the captured disassembly
7216+
dump(context, "\n");
7217+
dumpIndent(context);
7218+
dump(context, disassemblyOutput);
7219+
}
7220+
else
7221+
{
7222+
dump(context, "<disassembly failed>");
7223+
}
7224+
}
7225+
else
7226+
{
7227+
dump(context, "<invalid SPIR-V>");
7228+
}
7229+
}
7230+
else
7231+
{
7232+
dump(context, "<binary blob>");
7233+
}
7234+
dump(context, ")");
7235+
return;
7236+
}
7237+
71697238
// Special case the SPIR-V asm operands as the distinction here is
71707239
// clear anyway to the user
71717240
switch (op)

source/slang/slang.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -3747,6 +3747,18 @@ SlangResult EndToEndCompileRequest::executeActionsInner()
37473747
{
37483748
SLANG_RETURN_ON_FAIL(
37493749
translationUnit->getModule()->precompileForTarget(targetEnum, nullptr));
3750+
3751+
if (frontEndReq->optionSet.shouldDumpIR())
3752+
{
3753+
DiagnosticSinkWriter writer(frontEndReq->getSink());
3754+
3755+
dumpIR(
3756+
translationUnit->getModule()->getIRModule(),
3757+
frontEndReq->m_irDumpOptions,
3758+
"PRECOMPILE_FOR_TARGET_COMPLETE_ALL",
3759+
frontEndReq->getSourceManager(),
3760+
&writer);
3761+
}
37503762
}
37513763
}
37523764
}

tests/library/export-library-generics.slang

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
//TEST_IGNORE_FILE:
1+
// This test checks the SPIR-V output when compiling a library containing generics with embedded downstream IR.
2+
3+
//TEST:SIMPLE(filecheck=CHECK): -o tests/library/export-library-generics-test.slang-module -target spirv -embed-downstream-ir -profile lib_6_6 -incomplete-library -dump-ir -verbose-paths
24

35
// export-library-generics.slang
46

@@ -37,3 +39,10 @@ public int normalFunc(int a, float b)
3739
{
3840
return a - floor(b);
3941
}
42+
43+
// CHECK:[availableInDownstreamIR(6 : Int)]
44+
// CHECK:EmbeddedDownstreamIR(6 : Int,
45+
// CHECK: OpCapability Linkage
46+
// CHECK:OpDecorate %SLANG_ParameterGroup_Constants__init LinkageAttributes "_SR29export_2Dxlibrary_2Dxgenerics30SLANG_ParameterGroup_ConstantsR8_24xinitp2pi_fi_f" Export
47+
// CHECK: OpDecorate %MyType_myMethod LinkageAttributes "_SR29export_2Dxlibrary_2Dxgenerics6MyType8myMethodp1pi_ii" Export
48+
// CHECK: OpDecorate %normalFuncUsesGeneric LinkageAttributes "_SR29export_2Dxlibrary_2Dxgenerics21normalFuncUsesGenericp1pi_ii" Export

0 commit comments

Comments
 (0)