diff options
| author | jarcherNV <jarcher@nvidia.com> | 2025-06-06 14:30:06 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-06 14:30:06 -0700 |
| commit | 0d16228ae22fa2e1a00e62dc099eea08da7717fe (patch) | |
| tree | 067573914132892dc1336dcdea2c8b595f24f871 /source | |
| parent | 649d533727b31b28397ffb3a530e655ac3861547 (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')
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 // |
