Skip to content

Commit f59e0ef

Browse files
mkeshavaNVslangbot
andauthored
IR: Add SPIR-V disassembly for embedded downstream IR dumps (#6529)
* 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 #6513 * Add module-dual-target-verify test Fixes #6517 Adds a new test to verify that dxil and spirv targets are stored separately in the precompiled blob. * Fix review comments from cheneym2 * format code --------- Co-authored-by: slangbot <186143334+slangbot@users.noreply.github.com>
1 parent ff55a56 commit f59e0ef

9 files changed

+242
-9
lines changed

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

+14
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,17 @@ class DownstreamCompilerBase : public ComBaseObject, public IDownstreamCompiler
398401
return SLANG_FAIL;
399402
}
400403

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

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

+29
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ 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 disassembleWithResult(
53+
const uint32_t* contents,
54+
int contentsSize,
55+
String& outString) SLANG_OVERRIDE;
5256
int link(
5357
const uint32_t** modules,
5458
const uint32_t* moduleSizes,
@@ -71,6 +75,7 @@ class GlslangDownstreamCompiler : public DownstreamCompilerBase
7175
glslang_CompileFunc_1_2 m_compile_1_2 = nullptr;
7276
glslang_ValidateSPIRVFunc m_validate = nullptr;
7377
glslang_DisassembleSPIRVFunc m_disassemble = nullptr;
78+
glslang_DisassembleSPIRVWithResultFunc m_disassembleWithResult = nullptr;
7479
glslang_LinkSPIRVFunc m_link = nullptr;
7580

7681
ComPtr<ISlangSharedLibrary> m_sharedLibrary;
@@ -86,6 +91,8 @@ SlangResult GlslangDownstreamCompiler::init(ISlangSharedLibrary* library)
8691
m_validate = (glslang_ValidateSPIRVFunc)library->findFuncByName("glslang_validateSPIRV");
8792
m_disassemble =
8893
(glslang_DisassembleSPIRVFunc)library->findFuncByName("glslang_disassembleSPIRV");
94+
m_disassembleWithResult = (glslang_DisassembleSPIRVWithResultFunc)library->findFuncByName(
95+
"glslang_disassembleSPIRVWithResult");
8996
m_link = (glslang_LinkSPIRVFunc)library->findFuncByName("glslang_linkSPIRV");
9097

9198
if (m_compile_1_0 == nullptr && m_compile_1_1 == nullptr && m_compile_1_2 == nullptr)
@@ -316,6 +323,28 @@ SlangResult GlslangDownstreamCompiler::validate(const uint32_t* contents, int co
316323
return SLANG_FAIL;
317324
}
318325

326+
SlangResult GlslangDownstreamCompiler::disassembleWithResult(
327+
const uint32_t* contents,
328+
int contentsSize,
329+
String& outString)
330+
{
331+
if (m_disassembleWithResult == nullptr)
332+
{
333+
return SLANG_FAIL;
334+
}
335+
336+
char* resultString = nullptr;
337+
if (m_disassembleWithResult(contents, contentsSize, &resultString))
338+
{
339+
if (resultString)
340+
{
341+
outString = String(resultString);
342+
return SLANG_OK;
343+
}
344+
}
345+
return SLANG_FAIL;
346+
}
347+
319348
SlangResult GlslangDownstreamCompiler::disassemble(const uint32_t* contents, int contentsSize)
320349
{
321350
if (m_disassemble == nullptr)

source/slang-glslang/slang-glslang.cpp

+35-7
Original file line numberDiff line numberDiff line change
@@ -184,36 +184,64 @@ 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
190190
_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(
195+
const uint32_t* contents,
196+
int contentsSize,
197+
char** outString)
195198
{
196199
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
200+
spv_text text;
197201

198202
uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE;
199203
options |= SPV_BINARY_TO_TEXT_OPTION_COMMENT;
200-
options |= SPV_BINARY_TO_TEXT_OPTION_PRINT;
201-
options |= SPV_BINARY_TO_TEXT_OPTION_COLOR;
202204
options |= SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES;
205+
options |= SPV_BINARY_TO_TEXT_OPTION_INDENT;
203206

204207
spv_diagnostic diagnostic = nullptr;
205208
spv_context context = spvContextCreate(kDefaultEnvironment);
206209
spv_result_t error =
207-
spvBinaryToText(context, contents, contentsSize, options, nullptr, &diagnostic);
210+
spvBinaryToText(context, contents, contentsSize, options, &text, &diagnostic);
208211
spvContextDestroy(context);
209212
if (error)
210213
{
211214
spvDiagnosticPrint(diagnostic);
212215
spvDiagnosticDestroy(diagnostic);
213216
return false;
214217
}
218+
else
219+
{
220+
if (outString)
221+
{
222+
// Allocate memory for the output string and copy the result
223+
size_t len = text->length + 1; // +1 for null terminator
224+
*outString = new char[len];
225+
memcpy(*outString, text->str, text->length);
226+
(*outString)[text->length] = '\0'; // Ensure null termination
227+
}
228+
229+
spvTextDestroy(text);
230+
return true;
231+
}
232+
}
233+
215234

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

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

source/slang-glslang/slang-glslang.h

+4
Original file line numberDiff line numberDiff line change
@@ -166,5 +166,9 @@ 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)(
170+
const uint32_t* contents,
171+
int contentsSize,
172+
char** outString);
169173
typedef bool (*glslang_LinkSPIRVFunc)(glslang_LinkRequest* request);
170174
#endif

source/slang-llvm/slang-llvm.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
#include "clang/Basic/Stack.h"
32
#include "clang/Basic/TargetOptions.h"
43
#include "clang/Basic/Version.h"
@@ -147,6 +146,16 @@ class LLVMDownstreamCompiler : public ComBaseObject, public IDownstreamCompiler
147146
SLANG_UNUSED(contentsSize);
148147
return SLANG_FAIL;
149148
}
149+
virtual SLANG_NO_THROW SlangResult SLANG_MCALL disassembleWithResult(
150+
const uint32_t* contents,
151+
int contentsSize,
152+
String& outString) SLANG_OVERRIDE
153+
{
154+
SLANG_UNUSED(contents);
155+
SLANG_UNUSED(contentsSize);
156+
SLANG_UNUSED(outString);
157+
return SLANG_FAIL;
158+
}
150159

151160
LLVMDownstreamCompiler()
152161
: m_desc(

source/slang/slang-ir.cpp

+77
Original file line numberDiff line numberDiff line change
@@ -7117,6 +7117,76 @@ void dumpIRGeneric(IRDumpContext* context, IRGeneric* witnessTable)
71177117
dump(context, "}\n");
71187118
}
71197119

7120+
static void dumpEmbeddedDownstream(IRDumpContext* context, IRInst* inst)
7121+
{
7122+
auto targetInst = inst->getOperand(0);
7123+
auto blobInst = inst->getOperand(1);
7124+
7125+
// Get the target value
7126+
auto targetLit = as<IRIntLit>(targetInst);
7127+
if (!targetLit)
7128+
{
7129+
dump(context, "EmbeddedDownstreamIR(invalid target)");
7130+
return;
7131+
}
7132+
7133+
// Get the blob
7134+
auto blobLitInst = as<IRBlobLit>(blobInst);
7135+
if (!blobLitInst)
7136+
{
7137+
dump(context, "EmbeddedDownstreamIR(invalid blob)");
7138+
return;
7139+
}
7140+
7141+
dump(context, "EmbeddedDownstreamIR(");
7142+
dump(context, targetLit->getValue());
7143+
dump(context, " : Int, ");
7144+
7145+
// If target is SPIR-V (6), disassemble the blob
7146+
if (targetLit->getValue() == (IRIntegerValue)CodeGenTarget::SPIRV)
7147+
{
7148+
auto blob = blobLitInst->getStringSlice();
7149+
const uint32_t* spirvCode = (const uint32_t*)blob.begin();
7150+
const size_t spirvWordCount = blob.getLength() / sizeof(uint32_t);
7151+
7152+
// Get the compiler from the session through the module
7153+
auto module = inst->getModule();
7154+
auto session = module->getSession();
7155+
IDownstreamCompiler* compiler =
7156+
session->getOrLoadDownstreamCompiler(PassThroughMode::SpirvDis, nullptr);
7157+
7158+
if (compiler)
7159+
{
7160+
// Use glslang interface to disassemble with string output
7161+
String disassemblyOutput;
7162+
if (SLANG_SUCCEEDED(compiler->disassembleWithResult(
7163+
spirvCode,
7164+
int(spirvWordCount),
7165+
disassemblyOutput)))
7166+
{
7167+
// Dump the captured disassembly
7168+
dump(context, "\n");
7169+
dumpIndent(context);
7170+
dump(context, disassemblyOutput);
7171+
}
7172+
else
7173+
{
7174+
dump(context, "<disassembly failed>");
7175+
}
7176+
}
7177+
else
7178+
{
7179+
dump(context, "<unavailable disassembler>");
7180+
}
7181+
}
7182+
else
7183+
{
7184+
// TODO: Add DXIL disassembly call here.
7185+
dump(context, "<binary blob>");
7186+
}
7187+
dump(context, ")");
7188+
}
7189+
71207190
static void dumpInstExpr(IRDumpContext* context, IRInst* inst)
71217191
{
71227192
if (!inst)
@@ -7166,6 +7236,13 @@ static void dumpInstExpr(IRDumpContext* context, IRInst* inst)
71667236
}
71677237
}
71687238

7239+
// Special case EmbeddedDownstreamIR to show SPIR-V disassembly
7240+
if (op == kIROp_EmbeddedDownstreamIR)
7241+
{
7242+
dumpEmbeddedDownstream(context, inst);
7243+
return;
7244+
}
7245+
71697246
// Special case the SPIR-V asm operands as the distinction here is
71707247
// clear anyway to the user
71717248
switch (op)

source/slang/slang.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -3747,6 +3747,24 @@ 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+
3762+
dumpIR(
3763+
translationUnit->getModule()->getIRModule()->getModuleInst(),
3764+
frontEndReq->m_irDumpOptions,
3765+
frontEndReq->getSourceManager(),
3766+
&writer);
3767+
}
37503768
}
37513769
}
37523770
}

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
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// multi-target-module.slang
2+
// Test that a slang-module can store both SPIR-V and DXIL blobs separately
3+
4+
//TEST:SIMPLE(filecheck=CHECK): -o tests/modules/multi-target-module.slang-module -target dxil -embed-downstream-ir -target spirv -embed-downstream-ir -profile lib_6_6 -incomplete-library -dump-ir -verbose-paths
5+
6+
module multi_target_module;
7+
8+
// Simple function that will work on both SPIR-V and DXIL targets
9+
public float4 addVectors(float4 a, float4 b)
10+
{
11+
return a + b;
12+
}
13+
14+
// Another function that should be compatible with both targets
15+
public float3 normalizeVector(float3 v)
16+
{
17+
return normalize(v);
18+
}
19+
20+
[shader("compute")]
21+
[numthreads(8, 8, 1)]
22+
void main(uint3 dispatchThreadID : SV_DispatchThreadID)
23+
{
24+
float4 a = float4(1.0, 2.0, 3.0, 4.0);
25+
float4 b = float4(5.0, 6.0, 7.0, 8.0);
26+
27+
float4 result = addVectors(a, b);
28+
29+
float3 v = float3(1.0, 1.0, 1.0);
30+
float3 n = normalizeVector(v);
31+
}
32+
33+
// Check for the first occurrence of availableInDownstreamIR for addVectors in this section
34+
// Check that there are two entries, one for dxil and one for spirv.
35+
// CHECK: [availableInDownstreamIR(6 : Int)]
36+
// CHECK: [availableInDownstreamIR(10 : Int)]
37+
// CHECK: [public]
38+
// CHECK: [export("_S19multi_target_module10addVectorsp2pi_v4fi_v4fv4f")]
39+
40+
// Check for the second occurrence of availableInDownstreamIR for normalizeVector in this section
41+
// Check that there are two entries, one for dxil and one for spirv.
42+
// CHECK: [availableInDownstreamIR(6 : Int)]
43+
// CHECK: [availableInDownstreamIR(10 : Int)]
44+
// CHECK: [public]
45+
// CHECK: [export("_S19multi_target_module15normalizeVectorp1pi_v3fv3f")]

0 commit comments

Comments
 (0)