summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjarcherNV <jarcher@nvidia.com>2025-06-06 14:30:06 -0700
committerGitHub <noreply@github.com>2025-06-06 14:30:06 -0700
commit0d16228ae22fa2e1a00e62dc099eea08da7717fe (patch)
tree067573914132892dc1336dcdea2c8b595f24f871 /source
parent649d533727b31b28397ffb3a530e655ac3861547 (diff)
Add command line option for separate debug info (#7178)
* Add command line option for separate debug info Add command line arg -separate-debug-info which, if provided, produces both a .spv and a .dbg.spv file. The .dbg.spv file contains full debug info and the .spv file has all debug info stripped out. Also add a DebugBuildIdentifier instruction to store a unique hash in both the output files, so they can be more easily matched together. A matching API is provided to allow using the Slang API to retrieve a base and debug SPIRV as well as the debug build identifier string.
Diffstat (limited to 'source')
-rw-r--r--source/compiler-core/slang-artifact-associated-impl.cpp5
-rw-r--r--source/compiler-core/slang-artifact-associated-impl.h3
-rw-r--r--source/compiler-core/slang-artifact-associated.h3
-rw-r--r--source/compiler-core/slang-artifact-impl.cpp42
-rw-r--r--source/compiler-core/slang-artifact-impl.h6
-rw-r--r--source/compiler-core/slang-artifact.h5
-rw-r--r--source/compiler-core/slang-spirv-core-grammar.h1
-rw-r--r--source/slang-record-replay/record/slang-component-type.cpp26
-rw-r--r--source/slang-record-replay/record/slang-component-type.h9
-rw-r--r--source/slang-record-replay/record/slang-entrypoint.h21
-rw-r--r--source/slang-record-replay/record/slang-module.h21
-rw-r--r--source/slang-record-replay/record/slang-type-conformance.h21
-rw-r--r--source/slang/slang-compiler-options.h5
-rw-r--r--source/slang/slang-compiler.cpp91
-rw-r--r--source/slang/slang-compiler.h77
-rw-r--r--source/slang/slang-emit-c-like.cpp2
-rw-r--r--source/slang/slang-emit-spirv-ops-debug-info-ext.h22
-rw-r--r--source/slang/slang-emit-spirv.cpp16
-rw-r--r--source/slang/slang-emit.cpp341
-rw-r--r--source/slang/slang-ir-inst-defs.h1
-rw-r--r--source/slang/slang-ir-insts.h9
-rw-r--r--source/slang/slang-ir-link.cpp5
-rw-r--r--source/slang/slang-ir-strip-debug-info.cpp1
-rw-r--r--source/slang/slang-ir.cpp7
-rw-r--r--source/slang/slang-options.cpp14
-rw-r--r--source/slang/slang.cpp57
26 files changed, 790 insertions, 21 deletions
diff --git a/source/compiler-core/slang-artifact-associated-impl.cpp b/source/compiler-core/slang-artifact-associated-impl.cpp
index 88ed9f665..11bfec8bc 100644
--- a/source/compiler-core/slang-artifact-associated-impl.cpp
+++ b/source/compiler-core/slang-artifact-associated-impl.cpp
@@ -339,5 +339,10 @@ SlangResult ArtifactPostEmitMetadata::isParameterLocationUsed(
return SLANG_OK;
}
+const char* ArtifactPostEmitMetadata::getDebugBuildIdentifier()
+{
+ return m_debugBuildIdentifier.getBuffer();
+}
+
} // namespace Slang
diff --git a/source/compiler-core/slang-artifact-associated-impl.h b/source/compiler-core/slang-artifact-associated-impl.h
index 0418b2018..6ae01626a 100644
--- a/source/compiler-core/slang-artifact-associated-impl.h
+++ b/source/compiler-core/slang-artifact-associated-impl.h
@@ -198,6 +198,8 @@ public:
SlangUInt registerIndex, // `register` for D3D12, `binding` for Vulkan
bool& outUsed) SLANG_OVERRIDE;
+ SLANG_NO_THROW virtual const char* SLANG_MCALL getDebugBuildIdentifier() SLANG_OVERRIDE;
+
void* getInterface(const Guid& uuid);
void* getObject(const Guid& uuid);
@@ -208,6 +210,7 @@ public:
List<ShaderBindingRange> m_usedBindings;
List<String> m_exportedFunctionMangledNames;
+ String m_debugBuildIdentifier;
};
} // namespace Slang
diff --git a/source/compiler-core/slang-artifact-associated.h b/source/compiler-core/slang-artifact-associated.h
index c8e74f98b..05d3f8ec4 100644
--- a/source/compiler-core/slang-artifact-associated.h
+++ b/source/compiler-core/slang-artifact-associated.h
@@ -138,6 +138,9 @@ public:
/// Get the list of functions that were exported in the linked IR
SLANG_NO_THROW virtual Slice<String> SLANG_MCALL getExportedFunctionMangledNames() = 0;
+
+ /// Get the debug build identifier for a base and debug spirv pair
+ SLANG_NO_THROW virtual const char* SLANG_MCALL getDebugBuildIdentifier() = 0;
};
} // namespace Slang
diff --git a/source/compiler-core/slang-artifact-impl.cpp b/source/compiler-core/slang-artifact-impl.cpp
index 31c01f1de..1aed32cea 100644
--- a/source/compiler-core/slang-artifact-impl.cpp
+++ b/source/compiler-core/slang-artifact-impl.cpp
@@ -170,6 +170,48 @@ void Artifact::removeAt(ContainedKind kind, Index i)
}
}
+uint32_t Artifact::getItemCount()
+{
+ // Ignore the metadata when counting items, and the base artifact is item 0.
+ uint32_t count = 1;
+ for (auto artifact : m_associated)
+ if (artifact->getDesc().payload != ArtifactPayload::Metadata &&
+ artifact->getDesc().payload != ArtifactPayload::PostEmitMetadata)
+ count++;
+ return count;
+}
+
+SlangResult Artifact::getItemData(uint32_t index, slang::IBlob** outblob)
+{
+ // Item 0 is the base artifact, otherwise iterate and ignore the metadata.
+ if (index == 0)
+ return loadBlob(ArtifactKeep::Yes, outblob);
+
+ uint32_t count = 1;
+ for (auto artifact : m_associated)
+ {
+ if (artifact->getDesc().payload == ArtifactPayload::Metadata ||
+ artifact->getDesc().payload == ArtifactPayload::PostEmitMetadata)
+ continue;
+ if (count == index)
+ return artifact->loadBlob(ArtifactKeep::Yes, outblob);
+ count++;
+ }
+ return SLANG_FAIL;
+}
+
+SlangResult Artifact::getMetadata(slang::IMetadata** outMetadata)
+{
+ // Find and return the associated metadata artifact.
+ auto metadata = findAssociatedRepresentation<IArtifactPostEmitMetadata>(this);
+ if (!metadata)
+ return SLANG_E_NOT_AVAILABLE;
+
+ *outMetadata = static_cast<slang::IMetadata*>(metadata);
+ (*outMetadata)->addRef();
+ return SLANG_OK;
+}
+
SlangResult Artifact::getOrCreateRepresentation(
const Guid& typeGuid,
ArtifactKeep keep,
diff --git a/source/compiler-core/slang-artifact-impl.h b/source/compiler-core/slang-artifact-impl.h
index a307a9d6d..8df105953 100644
--- a/source/compiler-core/slang-artifact-impl.h
+++ b/source/compiler-core/slang-artifact-impl.h
@@ -94,6 +94,12 @@ public:
return ComPtr<IArtifact>(new Artifact(desc, name));
}
+ virtual SLANG_NO_THROW uint32_t SLANG_MCALL getItemCount() SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL
+ getItemData(uint32_t index, slang::IBlob** outblob) SLANG_OVERRIDE;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getMetadata(slang::IMetadata** outMetadata)
+ SLANG_OVERRIDE;
+
protected:
/// Ctor
Artifact(const Desc& desc, const UnownedStringSlice& name)
diff --git a/source/compiler-core/slang-artifact.h b/source/compiler-core/slang-artifact.h
index 46e8831ff..5f69182c3 100644
--- a/source/compiler-core/slang-artifact.h
+++ b/source/compiler-core/slang-artifact.h
@@ -407,8 +407,11 @@ considered as a kind of side channel to associate arbitrary data including tempo
artifact.
A `child artifact` belongs to the artifact, within the hierarchy of artifacts.
+
+This also uses the ICompileResult interface to more easily allow the Slang API to retrieve
+multiple associated artifacts in cases where both base and debug spirv are needed.
*/
-class IArtifact : public ICastable
+class IArtifact : public slang::ICompileResult
{
public:
SLANG_COM_INTERFACE(
diff --git a/source/compiler-core/slang-spirv-core-grammar.h b/source/compiler-core/slang-spirv-core-grammar.h
index e91caa79a..87ac898d8 100644
--- a/source/compiler-core/slang-spirv-core-grammar.h
+++ b/source/compiler-core/slang-spirv-core-grammar.h
@@ -6,6 +6,7 @@
#include "../core/slang-string.h"
#include <optional>
+#include <spirv/unified1/NonSemanticShaderDebugInfo100.h>
#include <spirv/unified1/spirv.h>
namespace Slang
diff --git a/source/slang-record-replay/record/slang-component-type.cpp b/source/slang-record-replay/record/slang-component-type.cpp
index 05fd68691..732721aa4 100644
--- a/source/slang-record-replay/record/slang-component-type.cpp
+++ b/source/slang-record-replay/record/slang-component-type.cpp
@@ -157,6 +157,32 @@ SLANG_NO_THROW SlangResult SLANG_MCALL IComponentTypeRecorder::getTargetMetadata
return m_actualComponentType->getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
}
+SLANG_NO_THROW SlangResult SLANG_MCALL IComponentTypeRecorder::getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics)
+{
+ // No need to record this call.
+ return m_actualComponentType->getEntryPointCompileResult(
+ entryPointIndex,
+ targetIndex,
+ outCompileResult,
+ outDiagnostics);
+}
+
+SLANG_NO_THROW SlangResult SLANG_MCALL IComponentTypeRecorder::getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics)
+{
+ // No need to record this call.
+ return m_actualComponentType->getTargetCompileResult(
+ targetIndex,
+ outCompileResult,
+ outDiagnostics);
+}
+
SLANG_NO_THROW SlangResult IComponentTypeRecorder::getResultAsFileSystem(
SlangInt entryPointIndex,
SlangInt targetIndex,
diff --git a/source/slang-record-replay/record/slang-component-type.h b/source/slang-record-replay/record/slang-component-type.h
index 2c854ee33..da9749d90 100644
--- a/source/slang-record-replay/record/slang-component-type.h
+++ b/source/slang-record-replay/record/slang-component-type.h
@@ -71,6 +71,15 @@ public:
SlangInt targetIndex,
slang::IMetadata** outMetadata,
slang::IBlob** outDiagnostics = nullptr) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) override;
+ virtual SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics = nullptr) override;
protected:
virtual ApiClassId getClassId() = 0;
diff --git a/source/slang-record-replay/record/slang-entrypoint.h b/source/slang-record-replay/record/slang-entrypoint.h
index f9906f3a2..93da5557a 100644
--- a/source/slang-record-replay/record/slang-entrypoint.h
+++ b/source/slang-record-replay/record/slang-entrypoint.h
@@ -96,6 +96,27 @@ public:
return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
}
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCompileResult(
+ entryPointIndex,
+ targetIndex,
+ outCompileResult,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
+ }
+
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
SlangInt entryPointIndex,
SlangInt targetIndex,
diff --git a/source/slang-record-replay/record/slang-module.h b/source/slang-record-replay/record/slang-module.h
index d9c83576d..fc2a790ed 100644
--- a/source/slang-record-replay/record/slang-module.h
+++ b/source/slang-record-replay/record/slang-module.h
@@ -114,6 +114,27 @@ public:
return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
}
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCompileResult(
+ entryPointIndex,
+ targetIndex,
+ outCompileResult,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
+ }
+
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
SlangInt entryPointIndex,
SlangInt targetIndex,
diff --git a/source/slang-record-replay/record/slang-type-conformance.h b/source/slang-record-replay/record/slang-type-conformance.h
index 12a0e4021..f43131b92 100644
--- a/source/slang-record-replay/record/slang-type-conformance.h
+++ b/source/slang-record-replay/record/slang-type-conformance.h
@@ -93,6 +93,27 @@ public:
return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
}
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCompileResult(
+ entryPointIndex,
+ targetIndex,
+ outCompileResult,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
+ }
+
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
SlangInt entryPointIndex,
SlangInt targetIndex,
diff --git a/source/slang/slang-compiler-options.h b/source/slang/slang-compiler-options.h
index dd413bee4..7205e1696 100644
--- a/source/slang/slang-compiler-options.h
+++ b/source/slang/slang-compiler-options.h
@@ -365,6 +365,11 @@ struct CompilerOptionSet
bool shouldHaveSourceMap() { return !getBoolOption(CompilerOptionName::DisableSourceMap); }
+ bool shouldEmitSeparateDebugInfo()
+ {
+ return getBoolOption(CompilerOptionName::EmitSeparateDebug);
+ }
+
FloatingPointMode getFloatingPointMode()
{
return getEnumOption<FloatingPointMode>(CompilerOptionName::FloatingPointMode);
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index 86dcf5d7f..4e114eb78 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -1860,6 +1860,23 @@ static CodeGenTarget _getIntermediateTarget(CodeGenTarget target)
}
}
+static IArtifact* _getSeparateDbgArtifact(IArtifact* artifact)
+{
+ if (!artifact)
+ return nullptr;
+
+ // The first associated artifact of kind ObjectCode and SPIRV payload should be the debug
+ // artifact.
+ for (auto* associated : artifact->getAssociated())
+ {
+ auto desc = associated->getDesc();
+ if (desc.kind == ArtifactKind::ObjectCode && desc.payload == ArtifactPayload::SPIRV)
+ return associated;
+ }
+
+ return nullptr;
+}
+
/// Function to simplify the logic around emitting, and dissassembling
SlangResult CodeGenContext::_emitEntryPoints(ComPtr<IArtifact>& outArtifact)
{
@@ -1890,6 +1907,32 @@ SlangResult CodeGenContext::_emitEntryPoints(ComPtr<IArtifact>& outArtifact)
getSink(),
disassemblyArtifact.writeRef()));
+ // Also disassemble the debug artifact if one exists.
+ auto debugArtifact = _getSeparateDbgArtifact(intermediateArtifact);
+ ComPtr<IArtifact> disassemblyDebugArtifact;
+ if (debugArtifact)
+ {
+ SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::dissassembleWithDownstream(
+ getSession(),
+ debugArtifact,
+ getSink(),
+ disassemblyDebugArtifact.writeRef()));
+ disassemblyDebugArtifact->setName(debugArtifact->getName());
+
+ // The disassembly needs both the metadata for the debug build identifier
+ // and the debug spirv to be associated with is.
+ for (auto associated : intermediateArtifact->getAssociated())
+ {
+ if (associated->getDesc().payload == ArtifactPayload::Metadata ||
+ associated->getDesc().payload == ArtifactPayload::PostEmitMetadata)
+ {
+ disassemblyArtifact->addAssociated(associated);
+ break;
+ }
+ }
+ disassemblyArtifact->addAssociated(disassemblyDebugArtifact);
+ }
+
outArtifact.swap(disassemblyArtifact);
return SLANG_OK;
}
@@ -2113,6 +2156,46 @@ SlangResult EndToEndCompileRequest::_maybeWriteArtifact(const String& path, IArt
return SLANG_OK;
}
+// These helper functions are used by the -separate-debug-info command line
+// arg to extract the associated artifact containing the debug SPIRV data
+// and save it to a file with a .dbg.spv extension.
+static String _getDebugSpvPath(const String& basePath)
+{
+ // Find the last occurrence of ".spv" at the end of the string.
+ static const char ext[] = ".spv";
+ static const char dbgExt[] = ".dbg.spv";
+ Index extLen = 4;
+ if (basePath.getLength() >= extLen && basePath.endsWith(ext))
+ {
+ // Replace the ".spv" extension with ".dbg.spv"
+ String prefix = String(basePath.subString(0, basePath.getLength() - extLen));
+ return prefix + dbgExt;
+ }
+ // If it doesn't end with .spv, just append .dbg.spv
+ return basePath + dbgExt;
+}
+
+SlangResult EndToEndCompileRequest::_maybeWriteDebugArtifact(
+ TargetProgram* targetProgram,
+ const String& path,
+ IArtifact* artifact)
+{
+ if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::EmitSeparateDebug))
+ {
+ const auto dbgArtifact = _getSeparateDbgArtifact(artifact);
+ // The artifact's name may have been set to the debug build id hash, use
+ // it as the filename if it exists.
+ String dbgPath = dbgArtifact->getName();
+ if (dbgPath.getLength() == 0)
+ dbgPath = _getDebugSpvPath(path);
+ else
+ dbgPath.append(".dbg.spv");
+ return _maybeWriteArtifact(dbgPath, dbgArtifact);
+ }
+
+ return SLANG_OK;
+}
+
IArtifact* TargetProgram::_createWholeProgramResult(
DiagnosticSink* sink,
EndToEndCompileRequest* endToEndReq)
@@ -2612,6 +2695,10 @@ void EndToEndCompileRequest::generateOutput()
const auto path = _getWholeProgramPath(targetReq);
_maybeWriteArtifact(path, artifact);
+
+ // If we are compiling separate debug info, check for the additional
+ // SPIRV artifact and write that if needed.
+ _maybeWriteDebugArtifact(targetProgram, path, artifact);
}
}
else
@@ -2624,6 +2711,10 @@ void EndToEndCompileRequest::generateOutput()
const auto path = _getEntryPointPath(targetReq, ee);
_maybeWriteArtifact(path, artifact);
+
+ // If we are compiling separate debug info, check for the additional
+ // SPIRV artifact and write that if needed.
+ _maybeWriteDebugArtifact(targetProgram, path, artifact);
}
}
}
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index 861719aac..24ddd00cc 100644
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -324,6 +324,16 @@ public:
slang::IMetadata** outMetadata,
slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics = nullptr) SLANG_OVERRIDE;
+
SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
SlangInt entryPointIndex,
SlangInt targetIndex,
@@ -974,6 +984,27 @@ public:
return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
}
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCompileResult(
+ entryPointIndex,
+ targetIndex,
+ outCompileResult,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
+ }
+
SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
SlangInt entryPointIndex,
SlangInt targetIndex,
@@ -1253,6 +1284,27 @@ public:
return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
}
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCompileResult(
+ entryPointIndex,
+ targetIndex,
+ outCompileResult,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
+ }
+
SLANG_NO_THROW SlangResult SLANG_MCALL getResultAsFileSystem(
SlangInt entryPointIndex,
SlangInt targetIndex,
@@ -1581,6 +1633,27 @@ public:
return Super::getTargetMetadata(targetIndex, outMetadata, outDiagnostics);
}
+ SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getEntryPointCompileResult(
+ entryPointIndex,
+ targetIndex,
+ outCompileResult,
+ outDiagnostics);
+ }
+
+ SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCompileResult(
+ SlangInt targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics) SLANG_OVERRIDE
+ {
+ return Super::getTargetCompileResult(targetIndex, outCompileResult, outDiagnostics);
+ }
+
/// Get a serialized representation of the checked module.
virtual SLANG_NO_THROW SlangResult SLANG_MCALL
serialize(ISlangBlob** outSerializedBlob) override;
@@ -3356,6 +3429,10 @@ private:
/// Maybe write the artifact to the path (if set), or stdout (if there is no container or path)
SlangResult _maybeWriteArtifact(const String& path, IArtifact* artifact);
+ SlangResult _maybeWriteDebugArtifact(
+ TargetProgram* targetProgram,
+ const String& path,
+ IArtifact* artifact);
SlangResult _writeArtifact(const String& path, IArtifact* artifact);
/// Adds any extra settings to complete a targetRequest
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp
index 0534b64d4..0092d159a 100644
--- a/source/slang/slang-emit-c-like.cpp
+++ b/source/slang/slang-emit-c-like.cpp
@@ -3210,6 +3210,7 @@ void CLikeSourceEmitter::_emitInst(IRInst* inst)
case kIROp_DebugNoScope:
case kIROp_DebugInlinedVariable:
case kIROp_DebugFunction:
+ case kIROp_DebugBuildIdentifier:
break;
case kIROp_Unmodified:
@@ -5214,6 +5215,7 @@ void CLikeSourceEmitter::ensureGlobalInst(
case kIROp_DebugSource:
case kIROp_DebugValue:
case kIROp_DebugInlinedVariable:
+ case kIROp_DebugBuildIdentifier:
return;
default:
break;
diff --git a/source/slang/slang-emit-spirv-ops-debug-info-ext.h b/source/slang/slang-emit-spirv-ops-debug-info-ext.h
index d8826afdc..90a0d44a8 100644
--- a/source/slang/slang-emit-spirv-ops-debug-info-ext.h
+++ b/source/slang/slang-emit-spirv-ops-debug-info-ext.h
@@ -681,4 +681,26 @@ SpvInst* emitOpDebugForwardRefsComposite(
flags);
}
+template<typename T>
+SpvInst* emitOpDebugBuildIdentifier(
+ SpvInstParent* parent,
+ IRInst* inst,
+ const T& idResultType,
+ SpvInst* set,
+ IRInst* buildIdentifier,
+ IRInst* flags)
+{
+ static_assert(isSingular<T>);
+ return emitInst(
+ parent,
+ inst,
+ SpvOpExtInst,
+ idResultType,
+ kResultID,
+ set,
+ SpvWord(105),
+ buildIdentifier,
+ flags);
+}
+
#endif // SLANG_IN_SPIRV_EMIT_CONTEXT
diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp
index 0a3dab78a..ec9857ecf 100644
--- a/source/slang/slang-emit-spirv.cpp
+++ b/source/slang/slang-emit-spirv.cpp
@@ -2284,6 +2284,18 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
}
return result;
}
+ case kIROp_DebugBuildIdentifier:
+ {
+ ensureExtensionDeclaration(UnownedStringSlice("SPV_KHR_non_semantic_info"));
+ auto debugBuildIdentifier = as<IRDebugBuildIdentifier>(inst);
+ return emitOpDebugBuildIdentifier(
+ getSection(SpvLogicalSectionID::GlobalVariables),
+ inst,
+ inst->getFullType(),
+ getNonSemanticDebugInfoExtInst(),
+ debugBuildIdentifier->getBuildIdentifier(),
+ debugBuildIdentifier->getFlags());
+ }
case kIROp_GetStringHash:
return emitGetStringHash(inst);
case kIROp_AttributedType:
@@ -9184,6 +9196,10 @@ SlangResult emitSPIRVFromIR(
{
context.ensureInst(inst);
}
+ if (as<IRDebugBuildIdentifier>(inst))
+ {
+ context.ensureInst(inst);
+ }
if (shouldPreserveParams && as<IRGlobalParam>(inst))
{
context.ensureInst(inst);
diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp
index 88533d938..c0c1ba75a 100644
--- a/source/slang/slang-emit.cpp
+++ b/source/slang/slang-emit.cpp
@@ -356,6 +356,7 @@ void calcRequiredLoweringPassSet(
case kIROp_DebugScope:
case kIROp_DebugNoScope:
case kIROp_DebugFunction:
+ case kIROp_DebugBuildIdentifier:
result.debugInfo = true;
break;
case kIROp_ResultType:
@@ -616,6 +617,21 @@ static void unexportNonEmbeddableIR(CodeGenTarget target, IRModule* irModule)
}
}
+// Helper function to convert a 20 byte SHA1 to a hexadecimal string,
+// needed for the build identifier instruction.
+String getBuildIdentifierString(ComponentType* component)
+{
+ ComPtr<ISlangBlob> hashBlob;
+ component->getEntryPointHash(0, 0, hashBlob.writeRef());
+
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(hashBlob->getBufferPointer());
+ size_t size = hashBlob->getBufferSize();
+ StringBuilder sb;
+ for (size_t i = 0; i < size; ++i)
+ sb << StringUtil::makeStringWithFormat("%02x", data[i]);
+ return sb.produceString();
+}
+
Result linkAndOptimizeIR(
CodeGenContext* codeGenContext,
LinkingAndOptimizationOptions const& options,
@@ -644,6 +660,19 @@ Result linkAndOptimizeIR(
auto irModule = outLinkedIR.module;
auto irEntryPoints = outLinkedIR.entryPoints;
+ // For now, only emit the debug build identifier if separate debug info is enabled
+ // and only if there are targets.
+ // TODO: We will ultimately need to change this to always emit the instruction.
+ if (targetCompilerOptions.shouldEmitSeparateDebugInfo())
+ {
+ // Build identifier is a hash of the source code and compile options.
+ String buildIdentifier = getBuildIdentifierString(codeGenContext->getProgram());
+ int buildIdentifierFlags = 0;
+ IRBuilder builder(irModule);
+ builder.setInsertInto(irModule->getModuleInst());
+ builder.emitDebugBuildIdentifier(buildIdentifier.getUnownedSlice(), buildIdentifierFlags);
+ }
+
#if 0
dumpIRIfEnabled(codeGenContext, irModule, "LINKED");
#endif
@@ -2104,22 +2133,256 @@ SlangResult emitSPIRVFromIR(
const List<IRFunc*>& irEntryPoints,
List<uint8_t>& spirvOut);
-SlangResult emitSPIRVForEntryPointsDirectly(
- CodeGenContext* codeGenContext,
- ComPtr<IArtifact>& outArtifact)
+// Helper class to assist in stepping through the SPIRV instructions.
+class SpirvInstructionHelper
{
- // Outside because we want to keep IR in scope whilst we are processing emits
- LinkedIR linkedIR;
- LinkingAndOptimizationOptions linkingAndOptimizationOptions;
- SLANG_RETURN_ON_FAIL(
- linkAndOptimizeIR(codeGenContext, linkingAndOptimizationOptions, linkedIR));
+public:
+ // The index of the SPIRV words in the blob.
+ // The first 5 words are the header as defined by the SPIRV spec.
+ enum SpvWordIndex
+ {
+ SPV_INDEX_MAGIC_NUMBER,
+ SPV_INDEX_VERSION_NUMBER,
+ SPV_INDEX_GENERATOR_NUMBER,
+ SPV_INDEX_BOUND,
+ SPV_INDEX_SCHEMA,
+ SPV_INDEX_INSTRUCTION_START,
+ };
+
+ // An instruction in the SPIRV blob. This points to the first word of the instruction.
+ struct SpvInstruction
+ {
+ SpvInstruction(SpvWord* word)
+ : word(word)
+ {
+ }
- auto irModule = linkedIR.module;
- auto irEntryPoints = linkedIR.entryPoints;
+ uint16_t getOpCode() const { return word[0] & 0xFFFF; }
+ uint16_t getWordCountForInst() const { return (word[0] >> 16) & 0xFFFF; }
+ SpvWord getOperand(uint32_t index) const { return word[index + 1]; }
+
+ // Helper function to interpret the instruction as a string and
+ // extract the string value.
+ String getStringFromInst() const
+ {
+ String result;
+ for (uint32_t i = 1; i < getWordCountForInst(); i++)
+ {
+ SpvWord op = getOperand(i);
+ for (int b = 0; b < 4; ++b)
+ {
+ char c = (char)((op >> (b * 8)) & 0xFF);
+ if (c == '\0')
+ return result;
+ result.append(c);
+ }
+ }
+ return result;
+ }
+
+ SpvWord* word = nullptr;
+ };
+
+ SpirvInstructionHelper() {}
+
+ // Load the SPIRV instructions from the artifact into a data blob that
+ // we can read.
+ SlangResult loadBlob(ComPtr<IArtifact>& artifact)
+ {
+ ComPtr<ISlangBlob> spirvBlob;
+ SlangResult res = artifact->loadBlob(ArtifactKeep::Yes, spirvBlob.writeRef());
+ if (SLANG_FAILED(res) || !spirvBlob)
+ return SLANG_FAIL;
+
+ // Populate the full array of SPIR-V words.
+ m_words.clear();
+ m_words.addRange(
+ reinterpret_cast<const SpvWord*>(spirvBlob->getBufferPointer()),
+ spirvBlob->getBufferSize() / sizeof(SpvWord));
+
+ // Populate the header words. These are the first 5 words of the SPIR-V
+ // blob and are treated differently from the rest of the instructions.
+ m_headerWords.clear();
+ m_headerWords.addRange(m_words.getBuffer(), SPV_INDEX_INSTRUCTION_START);
+
+ return SLANG_OK;
+ }
+
+ // Get the header words.
+ List<SpvWord> getHeaderWords() const { return m_headerWords; }
+
+ // Visit all SPIRV instructions (excluding header words), invoking the callback for each
+ // instruction. The callback should be a function or lambda with signature: void(const
+ // SpvInstruction&).
+ template<typename Func>
+ void visitInstructions(Func&& callback)
+ {
+ // Instructions start after the header (first 5 words)
+ constexpr size_t kHeaderWordCount =
+ static_cast<size_t>(SpvWordIndex::SPV_INDEX_INSTRUCTION_START);
+ size_t i = kHeaderWordCount;
+ while (i < (size_t)m_words.getCount())
+ {
+ SpvWord* wordPtr = m_words.getBuffer() + i;
+ SpvInstruction inst(wordPtr);
+ callback(inst);
+ uint16_t wordCount = inst.getWordCountForInst();
+ if (wordCount == 0)
+ break; // Prevent infinite loop on malformed input
+ i += wordCount;
+ }
+ }
+
+private:
+ // The full array of SPIRV words.
+ List<SpvWord> m_words;
+
+ // The header words.
+ List<SpvWord> m_headerWords;
+};
+
+// Helper function that takes an artifact populated with SPIRV instructions
+// after the spirv-opt step, and a previously created but empty
+// strippedArtifact. The artifact is unmodified, and the strippedArtifact
+// will contain all the artifact's instructions except for debug instructions.
+static SlangResult stripDbgSpirvFromArtifact(
+ ComPtr<IArtifact>& artifact,
+ ComPtr<IArtifact>& strippedArtifact)
+{
+ // Standard debug opcodes to strip out. This mimics the behavior of
+ // spirv-opt.
+ static const uint16_t debugOpCodeVals[] = {
+ SpvOpSourceContinued,
+ SpvOpSource,
+ SpvOpSourceExtension,
+ SpvOpString,
+ SpvOpName,
+ SpvOpMemberName,
+ SpvOpModuleProcessed,
+ SpvOpLine,
+ SpvOpNoLine};
+ // If the instruction is an extended instruction, then we also need
+ // to check if the instruction number is for a debug instruction as
+ // listed in slang-emit-spirv-ops-debug-info-ext.h
+ static const uint32_t debugExtInstVals[] = {
+ NonSemanticShaderDebugInfo100DebugCompilationUnit,
+ NonSemanticShaderDebugInfo100DebugTypeBasic,
+ NonSemanticShaderDebugInfo100DebugTypePointer,
+ NonSemanticShaderDebugInfo100DebugTypeQualifier,
+ NonSemanticShaderDebugInfo100DebugTypeArray,
+ NonSemanticShaderDebugInfo100DebugTypeVector,
+ NonSemanticShaderDebugInfo100DebugTypeFunction,
+ NonSemanticShaderDebugInfo100DebugTypeComposite,
+ NonSemanticShaderDebugInfo100DebugTypeMember,
+ NonSemanticShaderDebugInfo100DebugFunction,
+ NonSemanticShaderDebugInfo100DebugScope,
+ NonSemanticShaderDebugInfo100DebugNoScope,
+ NonSemanticShaderDebugInfo100DebugInlinedAt,
+ NonSemanticShaderDebugInfo100DebugLocalVariable,
+ NonSemanticShaderDebugInfo100DebugInlinedVariable,
+ NonSemanticShaderDebugInfo100DebugDeclare,
+ NonSemanticShaderDebugInfo100DebugValue,
+ NonSemanticShaderDebugInfo100DebugExpression,
+ NonSemanticShaderDebugInfo100DebugSource,
+ NonSemanticShaderDebugInfo100DebugFunctionDefinition,
+ NonSemanticShaderDebugInfo100DebugSourceContinued,
+ NonSemanticShaderDebugInfo100DebugLine,
+ NonSemanticShaderDebugInfo100DebugEntryPoint,
+ NonSemanticShaderDebugInfo100DebugTypeMatrix,
+ };
+
+ // Hash sets for easier lookup.
+ HashSet<uint16_t> debugOpCodes;
+ for (auto val : debugOpCodeVals)
+ debugOpCodes.add(val);
+ HashSet<uint32_t> debugExtInstNumbers;
+ for (auto val : debugExtInstVals)
+ debugExtInstNumbers.add(val);
+
+ SpirvInstructionHelper spirvInstructionHelper;
+ SLANG_RETURN_ON_FAIL(spirvInstructionHelper.loadBlob(artifact));
+
+ auto headerWords = spirvInstructionHelper.getHeaderWords();
+
+ List<uint8_t> spirvWordsList;
+ spirvWordsList.addRange(
+ reinterpret_cast<const uint8_t*>(headerWords.getBuffer()),
+ headerWords.getCount() * sizeof(SpvWord));
+
+ // First find the DebugBuildIdentifier instruction, and keep track of which string
+ // it refers to, this string needs to be kept in the final output.
+ SpvWord debugStringId = 0;
+ spirvInstructionHelper.visitInstructions(
+ [&](const SpirvInstructionHelper::SpvInstruction& inst)
+ {
+ if (inst.getOpCode() == SpvOpExtInst)
+ {
+ if (inst.getOperand(3) == NonSemanticShaderDebugInfo100DebugBuildIdentifier)
+ {
+ debugStringId = inst.getOperand(4);
+ return;
+ }
+ }
+ });
+
+ // Iterate over the instructions from the artifact and add them to the list
+ // only if they are not debug instructions. We also get the debug build hash
+ // to use as the filename for the debug spirv file.
+ String debugBuildHash;
+ spirvInstructionHelper.visitInstructions(
+ [&](const SpirvInstructionHelper::SpvInstruction& inst)
+ {
+ if (debugOpCodes.contains(inst.getOpCode()))
+ {
+ // We can only strip strings if they are not being used by the
+ // DebugBuildIdentifier instruction.
+ bool foundDebugString = false;
+ if (inst.getOpCode() == SpvOpString && inst.getOperand(0) == debugStringId)
+ {
+ debugBuildHash = inst.getStringFromInst();
+ foundDebugString = true;
+ }
+ if (!foundDebugString)
+ return;
+ }
+ // Also check if the instruction is an extended instruction containing DebugInfo.
+ if (inst.getOpCode() == SpvOpExtInst)
+ {
+ // Ignore this if the instruction contains DebugInfo.
+ if (debugExtInstNumbers.contains(inst.getOperand(3)))
+ return;
+ }
+ // Otherwise this is a non-debug instruction and should be included.
+ spirvWordsList.addRange(
+ reinterpret_cast<const uint8_t*>(inst.word),
+ inst.getWordCountForInst() * sizeof(SpvWord));
+ });
+ // Create the stripped artifact using the above created instruction list.
+ strippedArtifact->addRepresentationUnknown(ListBlob::moveCreate(spirvWordsList));
+
+ // Set the name of the artifact to the debug build hash so it can be used
+ // as the filename for the debug spirv file.
+ artifact->setName(debugBuildHash.getBuffer());
+
+ return SLANG_OK;
+}
+
+// Helper function to create an artifact from IR used internally by
+// emitSPIRVForEntryPointsDirectly.
+static SlangResult createArtifactFromIR(
+ CodeGenContext* codeGenContext,
+ IRModule* irModule,
+ List<IRFunc*> irEntryPoints,
+ ComPtr<IArtifact>& artifact,
+ ComPtr<IArtifact>& dbgArtifact)
+{
List<uint8_t> spirv, outSpirv;
emitSPIRVFromIR(codeGenContext, irModule, irEntryPoints, spirv);
+ auto targetRequest = codeGenContext->getTargetReq();
+ auto targetCompilerOptions = targetRequest->getOptionSet();
+
#if 0
String optErr;
if (SLANG_FAILED(optimizeSPIRV(spirv, optErr, outSpirv)))
@@ -2128,8 +2391,7 @@ SlangResult emitSPIRVForEntryPointsDirectly(
spirv = _Move(outSpirv);
}
#endif
- auto artifact =
- ArtifactUtil::createArtifactForCompileTarget(asExternal(codeGenContext->getTargetFormat()));
+
artifact->addRepresentationUnknown(ListBlob::moveCreate(spirv));
IDownstreamCompiler* compiler = codeGenContext->getSession()->getOrLoadDownstreamCompiler(
@@ -2252,7 +2514,19 @@ SlangResult emitSPIRVForEntryPointsDirectly(
auto downstreamStartTime = std::chrono::high_resolution_clock::now();
if (SLANG_SUCCEEDED(compiler->compile(downstreamOptions, optimizedArtifact.writeRef())))
{
- artifact = _Move(optimizedArtifact);
+ // Check if we need to output a separate SPIRV file containing debug info. If so
+ // then strip all debug instructions from the artifact. The dbgArtifact will still
+ // contain all instructions.
+ if (targetCompilerOptions.shouldEmitSeparateDebugInfo())
+ {
+ auto strippedArtifact = ArtifactUtil::createArtifactForCompileTarget(SLANG_SPIRV);
+ SLANG_RETURN_ON_FAIL(
+ stripDbgSpirvFromArtifact(optimizedArtifact, strippedArtifact));
+ artifact = _Move(strippedArtifact);
+ dbgArtifact = _Move(optimizedArtifact);
+ }
+ else
+ artifact = _Move(optimizedArtifact);
}
auto downstreamElapsedTime =
(std::chrono::high_resolution_clock::now() - downstreamStartTime).count() * 0.000000001;
@@ -2262,8 +2536,49 @@ SlangResult emitSPIRVForEntryPointsDirectly(
passthroughDownstreamDiagnostics(codeGenContext->getSink(), compiler, artifact));
}
+ return SLANG_OK;
+}
+
+SlangResult emitSPIRVForEntryPointsDirectly(
+ CodeGenContext* codeGenContext,
+ ComPtr<IArtifact>& outArtifact)
+{
+ // Outside because we want to keep IR in scope whilst we are processing emits
+ LinkedIR linkedIR;
+ LinkingAndOptimizationOptions linkingAndOptimizationOptions;
+ SLANG_RETURN_ON_FAIL(
+ linkAndOptimizeIR(codeGenContext, linkingAndOptimizationOptions, linkedIR));
+
+ auto irModule = linkedIR.module;
+ auto irEntryPoints = linkedIR.entryPoints;
+
+ dumpIRIfEnabled(codeGenContext, irModule, "POST LINK AND OPTIMIZE");
+
+ auto targetRequest = codeGenContext->getTargetReq();
+ auto targetCompilerOptions = targetRequest->getOptionSet();
+
+ // Create the artifact containing the main SPIRV data, and the debug SPIRV
+ // data if requested by the command line arg -separate-debug-info.
+ Slang::ComPtr<Slang::IArtifact> dbgArtifact;
+ auto artifact =
+ ArtifactUtil::createArtifactForCompileTarget(asExternal(codeGenContext->getTargetFormat()));
+ SLANG_RETURN_ON_FAIL(
+ createArtifactFromIR(codeGenContext, irModule, irEntryPoints, artifact, dbgArtifact));
ArtifactUtil::addAssociated(artifact, linkedIR.metadata);
+ // Associate the debug artifact with the main artifact.
+ // EndToEndCompileRequest::generateOutput will read this data
+ // and produce a .dbg.spv file for this child artifact.
+ if (targetCompilerOptions.shouldEmitSeparateDebugInfo())
+ {
+ artifact->addAssociated(dbgArtifact);
+
+ auto artifactPostEmitMetadata =
+ static_cast<ArtifactPostEmitMetadata*>(linkedIR.metadata.get());
+ artifactPostEmitMetadata->addRef();
+ artifactPostEmitMetadata->m_debugBuildIdentifier = dbgArtifact->getName();
+ }
+
outArtifact.swap(artifact);
return SLANG_OK;
diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h
index eac337deb..3711c3a31 100644
--- a/source/slang/slang-ir-inst-defs.h
+++ b/source/slang/slang-ir-inst-defs.h
@@ -1373,6 +1373,7 @@ INST(DebugFunction, DebugFunction, 5, 0)
INST(DebugInlinedVariable, DebugInlinedVariable, 2, 0)
INST(DebugScope, DebugScope, 2, 0)
INST(DebugNoScope, DebugNoScope, 1, 0)
+INST(DebugBuildIdentifier, DebugBuildIdentifier, 2, 0)
/* Embedded Precompiled Libraries */
INST(EmbeddedDownstreamIR, EmbeddedDownstreamIR, 2, 0)
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index a1f66d142..4f52bff5d 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -3462,6 +3462,13 @@ struct IRDebugSource : IRInst
IRInst* getSource() { return getOperand(1); }
};
+struct IRDebugBuildIdentifier : IRInst
+{
+ IR_LEAF_ISA(DebugBuildIdentifier)
+ IRInst* getBuildIdentifier() { return getOperand(0); }
+ IRInst* getFlags() { return getOperand(1); }
+};
+
struct IRDebugLine : IRInst
{
IR_LEAF_ISA(DebugLine)
@@ -4031,6 +4038,8 @@ public:
}
IRInst* emitDebugSource(UnownedStringSlice fileName, UnownedStringSlice source);
+ IRInst* emitDebugBuildIdentifier(UnownedStringSlice buildIdentifier, IRIntegerValue flags);
+ IRInst* emitDebugBuildIdentifier(IRInst* debugBuildIdentifier);
IRInst* emitDebugLine(
IRInst* source,
IRIntegerValue lineStart,
diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp
index 29a42b73c..a7b4c4560 100644
--- a/source/slang/slang-ir-link.cpp
+++ b/source/slang/slang-ir-link.cpp
@@ -2174,6 +2174,11 @@ LinkedIR linkIR(CodeGenContext* codeGenContext)
// regardless if the source files participate in the line table or not.
cloneValue(context, inst);
break;
+ case kIROp_DebugBuildIdentifier:
+ // The debug build identifier won't be referenced by anything,
+ // but we still need to keep it around if it is in the IR.
+ cloneValue(context, inst);
+ break;
}
}
}
diff --git a/source/slang/slang-ir-strip-debug-info.cpp b/source/slang/slang-ir-strip-debug-info.cpp
index 124c41a5c..4624a2f9e 100644
--- a/source/slang/slang-ir-strip-debug-info.cpp
+++ b/source/slang/slang-ir-strip-debug-info.cpp
@@ -17,6 +17,7 @@ static void findDebugInfo(IRInst* inst, List<IRInst*>& debugInstructions)
case kIROp_DebugScope:
case kIROp_DebugNoScope:
case kIROp_DebugFunction:
+ case kIROp_DebugBuildIdentifier:
debugInstructions.add(inst);
break;
default:
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index e66ad69ce..89d7a666e 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -3393,6 +3393,13 @@ IRInst* IRBuilder::emitDebugSource(UnownedStringSlice fileName, UnownedStringSli
IRInst* args[] = {getStringValue(fileName), getStringValue(source)};
return emitIntrinsicInst(getVoidType(), kIROp_DebugSource, 2, args);
}
+IRInst* IRBuilder::emitDebugBuildIdentifier(
+ UnownedStringSlice buildIdentifier,
+ IRIntegerValue flags)
+{
+ IRInst* args[] = {getStringValue(buildIdentifier), getIntValue(getUIntType(), flags)};
+ return emitIntrinsicInst(getVoidType(), kIROp_DebugBuildIdentifier, 2, args);
+}
IRInst* IRBuilder::emitDebugLine(
IRInst* source,
IRIntegerValue lineStart,
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index c269b10c4..68f538508 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -820,7 +820,11 @@ void initCommandOptions(CommandOptions& options)
"-verify-debug-serial-ir",
nullptr,
"Verify IR in the front-end."},
- {OptionKind::DumpModule, "-dump-module", nullptr, "Disassemble and print the module IR."}};
+ {OptionKind::DumpModule, "-dump-module", nullptr, "Disassemble and print the module IR."},
+ {OptionKind::EmitSeparateDebug,
+ "-separate-debug-info",
+ nullptr,
+ "Emit debug data to a separate file, and strip it from the main output file."}};
_addOptions(makeConstArrayView(debuggingOpts), options);
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Experimental !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
@@ -3079,6 +3083,14 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv)
break;
}
+ case OptionKind::EmitSeparateDebug:
+ {
+ // This will emit a separate debug file, containing all debug info in
+ // a .dbg.spv file. The main output SPIRV will have all debug info stripped.
+ m_compileRequest->setDebugInfoLevel(SLANG_DEBUG_INFO_LEVEL_MAXIMAL);
+ linkage->m_optionSet.set(OptionKind::EmitSeparateDebug, true);
+ break;
+ }
default:
{
// Hmmm, we looked up and produced a valid enum, but it wasn't handled in the
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index d8879c690..d66e86b2d 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -5499,12 +5499,16 @@ SLANG_NO_THROW void SLANG_MCALL ComponentType::getEntryPointHash(
buildHash(builder);
// Add the name and name override for the specified entry point to the hash.
- auto entryPointName = getEntryPoint(entryPointIndex)->getName()->text;
- builder.append(entryPointName);
- auto entryPointMangledName = getEntryPointMangledName(entryPointIndex);
- builder.append(entryPointMangledName);
- auto entryPointNameOverride = getEntryPointNameOverride(entryPointIndex);
- builder.append(entryPointNameOverride);
+ auto entryPoint = getEntryPoint(entryPointIndex);
+ if (entryPoint)
+ {
+ auto entryPointName = entryPoint->getName()->text;
+ builder.append(entryPointName);
+ auto entryPointMangledName = getEntryPointMangledName(entryPointIndex);
+ builder.append(entryPointMangledName);
+ auto entryPointNameOverride = getEntryPointNameOverride(entryPointIndex);
+ builder.append(entryPointNameOverride);
+ }
auto hash = builder.finalize().toBlob();
*outHash = hash.detach();
@@ -5567,6 +5571,33 @@ SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointMetadata(
return SLANG_OK;
}
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointCompileResult(
+ SlangInt entryPointIndex,
+ Int targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics)
+{
+ auto linkage = getLinkage();
+ if (targetIndex < 0 || targetIndex >= linkage->targets.getCount())
+ return SLANG_E_INVALID_ARG;
+ auto target = linkage->targets[targetIndex];
+
+ auto targetProgram = getTargetProgram(target);
+
+ DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
+ applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet);
+ applySettingsToDiagnosticSink(&sink, &sink, m_optionSet);
+
+ IArtifact* artifact = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink);
+ sink.getBlobIfNeeded(outDiagnostics);
+ if (artifact == nullptr)
+ return SLANG_E_NOT_AVAILABLE;
+
+ *outCompileResult = static_cast<slang::ICompileResult*>(artifact);
+ (*outCompileResult)->addRef();
+ return SLANG_OK;
+}
+
RefPtr<ComponentType> ComponentType::specialize(
SpecializationArg const* inSpecializationArgs,
SlangInt specializationArgCount,
@@ -5902,6 +5933,20 @@ SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getTargetMetadata(
return SLANG_OK;
}
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getTargetCompileResult(
+ Int targetIndex,
+ slang::ICompileResult** outCompileResult,
+ slang::IBlob** outDiagnostics)
+{
+ IArtifact* artifact = getTargetArtifact(targetIndex, outDiagnostics);
+ if (artifact == nullptr)
+ return SLANG_E_NOT_AVAILABLE;
+
+ *outCompileResult = static_cast<slang::ICompileResult*>(artifact);
+ //(*outCompileResult)->addRef(); // TODO: Needed if using a ComPtr.
+ return SLANG_OK;
+}
+
//
// CompositeComponentType
//