summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/compiler-core/slang-downstream-compiler.h14
-rw-r--r--source/compiler-core/slang-glslang-compiler.cpp29
-rw-r--r--source/slang-glslang/slang-glslang.cpp42
-rw-r--r--source/slang-glslang/slang-glslang.h4
-rw-r--r--source/slang-llvm/slang-llvm.cpp11
-rw-r--r--source/slang/slang-ir.cpp77
-rw-r--r--source/slang/slang.cpp18
-rw-r--r--tests/library/export-library-generics.slang11
-rw-r--r--tests/modules/multi-target-module.slang45
9 files changed, 242 insertions, 9 deletions
diff --git a/source/compiler-core/slang-downstream-compiler.h b/source/compiler-core/slang-downstream-compiler.h
index c96003cc4..5365b9839 100644
--- a/source/compiler-core/slang-downstream-compiler.h
+++ b/source/compiler-core/slang-downstream-compiler.h
@@ -340,6 +340,9 @@ public:
/// Disassemble and print to stdout
virtual SLANG_NO_THROW SlangResult SLANG_MCALL
disassemble(const uint32_t* contents, int contentsSize) = 0;
+ /// Disassemble and return the result as a string
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ disassembleWithResult(const uint32_t* contents, int contentsSize, String& outString) = 0;
/// True if underlying compiler uses file system to communicate source
virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased() = 0;
@@ -398,6 +401,17 @@ public:
return SLANG_FAIL;
}
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL disassembleWithResult(
+ const uint32_t* contents,
+ int contentsSize,
+ String& outString) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(contents);
+ SLANG_UNUSED(contentsSize);
+ SLANG_UNUSED(outString);
+ return SLANG_FAIL;
+ }
+
DownstreamCompilerBase(const Desc& desc)
: m_desc(desc)
{
diff --git a/source/compiler-core/slang-glslang-compiler.cpp b/source/compiler-core/slang-glslang-compiler.cpp
index 540b437c5..27a24512d 100644
--- a/source/compiler-core/slang-glslang-compiler.cpp
+++ b/source/compiler-core/slang-glslang-compiler.cpp
@@ -49,6 +49,10 @@ public:
validate(const uint32_t* contents, int contentsSize) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL
disassemble(const uint32_t* contents, int contentsSize) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL disassembleWithResult(
+ const uint32_t* contents,
+ int contentsSize,
+ String& outString) SLANG_OVERRIDE;
int link(
const uint32_t** modules,
const uint32_t* moduleSizes,
@@ -71,6 +75,7 @@ protected:
glslang_CompileFunc_1_2 m_compile_1_2 = nullptr;
glslang_ValidateSPIRVFunc m_validate = nullptr;
glslang_DisassembleSPIRVFunc m_disassemble = nullptr;
+ glslang_DisassembleSPIRVWithResultFunc m_disassembleWithResult = nullptr;
glslang_LinkSPIRVFunc m_link = nullptr;
ComPtr<ISlangSharedLibrary> m_sharedLibrary;
@@ -86,6 +91,8 @@ SlangResult GlslangDownstreamCompiler::init(ISlangSharedLibrary* library)
m_validate = (glslang_ValidateSPIRVFunc)library->findFuncByName("glslang_validateSPIRV");
m_disassemble =
(glslang_DisassembleSPIRVFunc)library->findFuncByName("glslang_disassembleSPIRV");
+ m_disassembleWithResult = (glslang_DisassembleSPIRVWithResultFunc)library->findFuncByName(
+ "glslang_disassembleSPIRVWithResult");
m_link = (glslang_LinkSPIRVFunc)library->findFuncByName("glslang_linkSPIRV");
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
return SLANG_FAIL;
}
+SlangResult GlslangDownstreamCompiler::disassembleWithResult(
+ const uint32_t* contents,
+ int contentsSize,
+ String& outString)
+{
+ if (m_disassembleWithResult == nullptr)
+ {
+ return SLANG_FAIL;
+ }
+
+ char* resultString = nullptr;
+ if (m_disassembleWithResult(contents, contentsSize, &resultString))
+ {
+ if (resultString)
+ {
+ outString = String(resultString);
+ return SLANG_OK;
+ }
+ }
+ return SLANG_FAIL;
+}
+
SlangResult GlslangDownstreamCompiler::disassemble(const uint32_t* contents, int contentsSize)
{
if (m_disassemble == nullptr)
diff --git a/source/slang-glslang/slang-glslang.cpp b/source/slang-glslang/slang-glslang.cpp
index 3dc9b1bd4..8d9f64fd5 100644
--- a/source/slang-glslang/slang-glslang.cpp
+++ b/source/slang-glslang/slang-glslang.cpp
@@ -184,27 +184,30 @@ extern "C"
return tools.Validate(contents, contentsSize, options);
}
-// Disassemble the given SPIRV-ASM instructions.
+// Disassemble the given SPIRV-ASM instructions and return the result as a string.
extern "C"
#ifdef _MSC_VER
_declspec(dllexport)
#else
- __attribute__((__visibility__("default")))
+__attribute__((__visibility__("default")))
#endif
- bool glslang_disassembleSPIRV(const uint32_t* contents, int contentsSize)
+ bool glslang_disassembleSPIRVWithResult(
+ const uint32_t* contents,
+ int contentsSize,
+ char** outString)
{
static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5;
+ spv_text text;
uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE;
options |= SPV_BINARY_TO_TEXT_OPTION_COMMENT;
- options |= SPV_BINARY_TO_TEXT_OPTION_PRINT;
- options |= SPV_BINARY_TO_TEXT_OPTION_COLOR;
options |= SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES;
+ options |= SPV_BINARY_TO_TEXT_OPTION_INDENT;
spv_diagnostic diagnostic = nullptr;
spv_context context = spvContextCreate(kDefaultEnvironment);
spv_result_t error =
- spvBinaryToText(context, contents, contentsSize, options, nullptr, &diagnostic);
+ spvBinaryToText(context, contents, contentsSize, options, &text, &diagnostic);
spvContextDestroy(context);
if (error)
{
@@ -212,8 +215,33 @@ extern "C"
spvDiagnosticDestroy(diagnostic);
return false;
}
+ else
+ {
+ if (outString)
+ {
+ // Allocate memory for the output string and copy the result
+ size_t len = text->length + 1; // +1 for null terminator
+ *outString = new char[len];
+ memcpy(*outString, text->str, text->length);
+ (*outString)[text->length] = '\0'; // Ensure null termination
+ }
+
+ spvTextDestroy(text);
+ return true;
+ }
+}
+
- return true;
+// Disassemble the given SPIRV-ASM instructions.
+extern "C"
+#ifdef _MSC_VER
+ _declspec(dllexport)
+#else
+__attribute__((__visibility__("default")))
+#endif
+ bool glslang_disassembleSPIRV(const uint32_t* contents, int contentsSize)
+{
+ return glslang_disassembleSPIRVWithResult(contents, contentsSize, nullptr);
}
// Apply the SPIRV-Tools optimizer to generated SPIR-V based on the desired optimization level
diff --git a/source/slang-glslang/slang-glslang.h b/source/slang-glslang/slang-glslang.h
index 8343b4c8e..d6e10d320 100644
--- a/source/slang-glslang/slang-glslang.h
+++ b/source/slang-glslang/slang-glslang.h
@@ -166,5 +166,9 @@ typedef int (*glslang_CompileFunc_1_1)(glslang_CompileRequest_1_1* request);
typedef int (*glslang_CompileFunc_1_2)(glslang_CompileRequest_1_2* request);
typedef bool (*glslang_ValidateSPIRVFunc)(const uint32_t* contents, int contentsSize);
typedef bool (*glslang_DisassembleSPIRVFunc)(const uint32_t* contents, int contentsSize);
+typedef bool (*glslang_DisassembleSPIRVWithResultFunc)(
+ const uint32_t* contents,
+ int contentsSize,
+ char** outString);
typedef bool (*glslang_LinkSPIRVFunc)(glslang_LinkRequest* request);
#endif
diff --git a/source/slang-llvm/slang-llvm.cpp b/source/slang-llvm/slang-llvm.cpp
index 86565a485..e645fb3c7 100644
--- a/source/slang-llvm/slang-llvm.cpp
+++ b/source/slang-llvm/slang-llvm.cpp
@@ -1,4 +1,3 @@
-
#include "clang/Basic/Stack.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/Version.h"
@@ -147,6 +146,16 @@ public:
SLANG_UNUSED(contentsSize);
return SLANG_FAIL;
}
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL disassembleWithResult(
+ const uint32_t* contents,
+ int contentsSize,
+ String& outString) SLANG_OVERRIDE
+ {
+ SLANG_UNUSED(contents);
+ SLANG_UNUSED(contentsSize);
+ SLANG_UNUSED(outString);
+ return SLANG_FAIL;
+ }
LLVMDownstreamCompiler()
: m_desc(
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index 72313217b..92e51fe3b 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -7117,6 +7117,76 @@ void dumpIRGeneric(IRDumpContext* context, IRGeneric* witnessTable)
dump(context, "}\n");
}
+static void dumpEmbeddedDownstream(IRDumpContext* context, IRInst* inst)
+{
+ auto targetInst = inst->getOperand(0);
+ auto blobInst = inst->getOperand(1);
+
+ // Get the target value
+ auto targetLit = as<IRIntLit>(targetInst);
+ if (!targetLit)
+ {
+ dump(context, "EmbeddedDownstreamIR(invalid target)");
+ return;
+ }
+
+ // Get the blob
+ auto blobLitInst = as<IRBlobLit>(blobInst);
+ if (!blobLitInst)
+ {
+ dump(context, "EmbeddedDownstreamIR(invalid blob)");
+ return;
+ }
+
+ dump(context, "EmbeddedDownstreamIR(");
+ dump(context, targetLit->getValue());
+ dump(context, " : Int, ");
+
+ // If target is SPIR-V (6), disassemble the blob
+ if (targetLit->getValue() == (IRIntegerValue)CodeGenTarget::SPIRV)
+ {
+ auto blob = blobLitInst->getStringSlice();
+ const uint32_t* spirvCode = (const uint32_t*)blob.begin();
+ const size_t spirvWordCount = blob.getLength() / sizeof(uint32_t);
+
+ // Get the compiler from the session through the module
+ auto module = inst->getModule();
+ auto session = module->getSession();
+ IDownstreamCompiler* compiler =
+ session->getOrLoadDownstreamCompiler(PassThroughMode::SpirvDis, nullptr);
+
+ if (compiler)
+ {
+ // Use glslang interface to disassemble with string output
+ String disassemblyOutput;
+ if (SLANG_SUCCEEDED(compiler->disassembleWithResult(
+ spirvCode,
+ int(spirvWordCount),
+ disassemblyOutput)))
+ {
+ // Dump the captured disassembly
+ dump(context, "\n");
+ dumpIndent(context);
+ dump(context, disassemblyOutput);
+ }
+ else
+ {
+ dump(context, "<disassembly failed>");
+ }
+ }
+ else
+ {
+ dump(context, "<unavailable disassembler>");
+ }
+ }
+ else
+ {
+ // TODO: Add DXIL disassembly call here.
+ dump(context, "<binary blob>");
+ }
+ dump(context, ")");
+}
+
static void dumpInstExpr(IRDumpContext* context, IRInst* inst)
{
if (!inst)
@@ -7166,6 +7236,13 @@ static void dumpInstExpr(IRDumpContext* context, IRInst* inst)
}
}
+ // Special case EmbeddedDownstreamIR to show SPIR-V disassembly
+ if (op == kIROp_EmbeddedDownstreamIR)
+ {
+ dumpEmbeddedDownstream(context, inst);
+ return;
+ }
+
// Special case the SPIR-V asm operands as the distinction here is
// clear anyway to the user
switch (op)
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index e17d7ba9c..a23342458 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -3747,6 +3747,24 @@ SlangResult EndToEndCompileRequest::executeActionsInner()
{
SLANG_RETURN_ON_FAIL(
translationUnit->getModule()->precompileForTarget(targetEnum, nullptr));
+
+ if (frontEndReq->optionSet.shouldDumpIR())
+ {
+ DiagnosticSinkWriter writer(frontEndReq->getSink());
+
+ dumpIR(
+ translationUnit->getModule()->getIRModule(),
+ frontEndReq->m_irDumpOptions,
+ "PRECOMPILE_FOR_TARGET_COMPLETE_ALL",
+ frontEndReq->getSourceManager(),
+ &writer);
+
+ dumpIR(
+ translationUnit->getModule()->getIRModule()->getModuleInst(),
+ frontEndReq->m_irDumpOptions,
+ frontEndReq->getSourceManager(),
+ &writer);
+ }
}
}
}
diff --git a/tests/library/export-library-generics.slang b/tests/library/export-library-generics.slang
index 3f17e0664..de683031e 100644
--- a/tests/library/export-library-generics.slang
+++ b/tests/library/export-library-generics.slang
@@ -1,4 +1,6 @@
-//TEST_IGNORE_FILE:
+// This test checks the SPIR-V output when compiling a library containing generics with embedded downstream IR.
+
+//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
// export-library-generics.slang
@@ -37,3 +39,10 @@ public int normalFunc(int a, float b)
{
return a - floor(b);
}
+
+// CHECK:[availableInDownstreamIR(6 : Int)]
+// CHECK:EmbeddedDownstreamIR(6 : Int,
+// CHECK: OpCapability Linkage
+// CHECK:OpDecorate %SLANG_ParameterGroup_Constants__init LinkageAttributes "_SR29export_2Dxlibrary_2Dxgenerics30SLANG_ParameterGroup_ConstantsR8_24xinitp2pi_fi_f" Export
+// CHECK: OpDecorate %MyType_myMethod LinkageAttributes "_SR29export_2Dxlibrary_2Dxgenerics6MyType8myMethodp1pi_ii" Export
+// CHECK: OpDecorate %normalFuncUsesGeneric LinkageAttributes "_SR29export_2Dxlibrary_2Dxgenerics21normalFuncUsesGenericp1pi_ii" Export \ No newline at end of file
diff --git a/tests/modules/multi-target-module.slang b/tests/modules/multi-target-module.slang
new file mode 100644
index 000000000..68b774b87
--- /dev/null
+++ b/tests/modules/multi-target-module.slang
@@ -0,0 +1,45 @@
+// multi-target-module.slang
+ // Test that a slang-module can store both SPIR-V and DXIL blobs separately
+
+ //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
+
+ module multi_target_module;
+
+ // Simple function that will work on both SPIR-V and DXIL targets
+ public float4 addVectors(float4 a, float4 b)
+ {
+ return a + b;
+ }
+
+ // Another function that should be compatible with both targets
+ public float3 normalizeVector(float3 v)
+ {
+ return normalize(v);
+ }
+
+ [shader("compute")]
+ [numthreads(8, 8, 1)]
+ void main(uint3 dispatchThreadID : SV_DispatchThreadID)
+ {
+ float4 a = float4(1.0, 2.0, 3.0, 4.0);
+ float4 b = float4(5.0, 6.0, 7.0, 8.0);
+
+ float4 result = addVectors(a, b);
+
+ float3 v = float3(1.0, 1.0, 1.0);
+ float3 n = normalizeVector(v);
+ }
+
+ // Check for the first occurrence of availableInDownstreamIR for addVectors in this section
+ // Check that there are two entries, one for dxil and one for spirv.
+ // CHECK: [availableInDownstreamIR(6 : Int)]
+ // CHECK: [availableInDownstreamIR(10 : Int)]
+ // CHECK: [public]
+ // CHECK: [export("_S19multi_target_module10addVectorsp2pi_v4fi_v4fv4f")]
+
+ // Check for the second occurrence of availableInDownstreamIR for normalizeVector in this section
+ // Check that there are two entries, one for dxil and one for spirv.
+ // CHECK: [availableInDownstreamIR(6 : Int)]
+ // CHECK: [availableInDownstreamIR(10 : Int)]
+ // CHECK: [public]
+ // CHECK: [export("_S19multi_target_module15normalizeVectorp1pi_v3fv3f")] \ No newline at end of file