diff options
| author | cheneym2 <acheney@nvidia.com> | 2024-08-05 15:37:46 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-05 15:37:46 -0400 |
| commit | d72f9f6f72a7a74d7466a1e301e1853fea5daa25 (patch) | |
| tree | 4ae77e2dd622779b64d063d1f50fc7af8c13a94a /source/slang | |
| parent | d63f5e20f1edf7c51ca5c456baceb9eb9a84c95b (diff) | |
Initial support for precompiled DXIL in slang-modules (#4755)
* Add embedded precompiled binary IR ops
Add IR operations to embed precompiled DXIL or SPIR-V blobs
into IR. Adds a BlobLit literal that is mostly identical to
StringLit except for its inability to be displayed, e.g.
in dumped IR. In the future, the blob might be dumped as
hexadecimal, but for now it is summarized as "<binary blob>".
* EmbeddedDXIL and SPIR-V options
The options, '-embed-dxil' and '-embed-spirv' in slangc, will
cause a target dxil or spirv to be compiled and stored in the
translation unit IR when written to a slang-module. Subsequent
changes actually implement the options.
* Per-translation unit DXIL precompilation
When -embed-dxil is specified, perform a precompilation to DXIL of
each TU, linked only with stdlib. Embed the resulting DXIL for
the TU in a IR op. Being part of IR, the precompiled DXIL can be
serialized to disk in a slang-module.
Upon loading slang-modules, the new IR op will be searched for and
the precompiled DXIL blob is saved with the loaded Module. During
linking, if all the Modules have precompiled blobs they will be
sent to the downstream compile commands as libraries instead of
source, skipping the downstream compilation, using DXC only for
linking.
Fixes Issue #4580
* Remove placeholder embedded SPIRV support
Code was added only to sketch out how other precompiled bins
will be supported.
* Remove the rest of the SPIRV placeholder support
* Fix warnings, test error on non-windows
* Remove lib_6_6 hack, add dxil_lib capability
* Allocate blob value from irmodule memarena
* Add null check after memarena allocation
* Restore the request->e2erequest code path for generatewholeprogram
* Update capability handling, move EmbedDXIL enum to end to preserve abi
* Remove lib_6_6 hack
* Move ICompileRequest functions to end
Diffstat (limited to 'source/slang')
| -rw-r--r-- | source/slang/slang-capabilities.capdef | 2 | ||||
| -rw-r--r-- | source/slang/slang-compiler-tu.cpp | 115 | ||||
| -rw-r--r-- | source/slang/slang-compiler.cpp | 59 | ||||
| -rwxr-xr-x | source/slang/slang-compiler.h | 14 | ||||
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-ir-inst-defs.h | 4 | ||||
| -rw-r--r-- | source/slang/slang-ir-insts.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-ir.cpp | 46 | ||||
| -rw-r--r-- | source/slang/slang-ir.h | 8 | ||||
| -rw-r--r-- | source/slang/slang-module-library.cpp | 9 | ||||
| -rw-r--r-- | source/slang/slang-options.cpp | 35 | ||||
| -rw-r--r-- | source/slang/slang-serialize-container.cpp | 7 | ||||
| -rw-r--r-- | source/slang/slang-serialize-ir.cpp | 9 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 28 |
14 files changed, 333 insertions, 9 deletions
diff --git a/source/slang/slang-capabilities.capdef b/source/slang/slang-capabilities.capdef index 264748738..220e4a424 100644 --- a/source/slang/slang-capabilities.capdef +++ b/source/slang/slang-capabilities.capdef @@ -119,6 +119,8 @@ def _sm_6_7 : _sm_6_6; def hlsl_nvapi : hlsl; +alias dxil_lib = _sm_6_3; + // cuda versions def _cuda_sm_1_0 : cuda; def _cuda_sm_2_0 : _cuda_sm_1_0; diff --git a/source/slang/slang-compiler-tu.cpp b/source/slang/slang-compiler-tu.cpp new file mode 100644 index 000000000..b007e7b71 --- /dev/null +++ b/source/slang/slang-compiler-tu.cpp @@ -0,0 +1,115 @@ +// slang-compiler-tu.cpp: Compiles translation units to target language +// and emit precompiled blobs into IR + +#include "../core/slang-basic.h" +#include "slang-compiler.h" +#include "slang-ir-insts.h" +#include "slang-capability.h" + +namespace Slang +{ + SLANG_NO_THROW SlangResult SLANG_MCALL Module::precompileForTargets( + DiagnosticSink* sink, + EndToEndCompileRequest* endToEndReq, + TargetRequest* targetReq) + { + auto module = getIRModule(); + Slang::Session* session = endToEndReq->getSession(); + Slang::ASTBuilder* astBuilder = session->getGlobalASTBuilder(); + Slang::Linkage* builtinLinkage = session->getBuiltinLinkage(); + Slang::Linkage linkage(session, astBuilder, builtinLinkage); + + CapabilityName precompileRequirement = CapabilityName::Invalid; + switch (targetReq->getTarget()) + { + case CodeGenTarget::DXIL: + linkage.addTarget(Slang::CodeGenTarget::DXIL); + precompileRequirement = CapabilityName::dxil_lib; + break; + default: + assert(!"Unhandled target"); + break; + } + SLANG_ASSERT(precompileRequirement != CapabilityName::Invalid); + + // Ensure precompilation capability requirements are met. + auto targetCaps = targetReq->getTargetCaps(); + auto precompileRequirementsCapabilitySet = CapabilitySet(precompileRequirement); + if (targetCaps.atLeastOneSetImpliedInOther(precompileRequirementsCapabilitySet) == CapabilitySet::ImpliesReturnFlags::NotImplied) + { + // If `RestrictiveCapabilityCheck` is true we will error, else we will warn. + // error ...: dxil libraries require $0, entry point compiled with $1. + // warn ...: dxil libraries require $0, entry point compiled with $1, implicitly upgrading capabilities. + maybeDiagnoseWarningOrError( + sink, + targetReq->getOptionSet(), + DiagnosticCategory::Capability, + SourceLoc(), + Diagnostics::incompatibleWithPrecompileLib, + Diagnostics::incompatibleWithPrecompileLibRestrictive, + precompileRequirementsCapabilitySet, + targetCaps); + + // add precompile requirements to the cooked targetCaps + targetCaps.join(precompileRequirementsCapabilitySet); + if (targetCaps.isInvalid()) + { + sink->diagnose(SourceLoc(), Diagnostics::unknownCapability, targetCaps); + return SLANG_FAIL; + } + else + { + targetReq->setTargetCaps(targetCaps); + } + } + + List<RefPtr<ComponentType>> allComponentTypes; + allComponentTypes.add(this); // Add Module as a component type + + for (auto entryPoint : this->getEntryPoints()) + { + allComponentTypes.add(entryPoint); // Add the entry point as a component type + } + + auto composite = CompositeComponentType::create( + &linkage, + allComponentTypes); + + TargetProgram tp(composite, targetReq); + tp.getOrCreateLayout(sink); + Slang::Index const entryPointCount = m_entryPoints.getCount(); + + CodeGenContext::EntryPointIndices entryPointIndices; + + entryPointIndices.setCount(entryPointCount); + for (Index i = 0; i < entryPointCount; i++) + entryPointIndices[i] = i; + CodeGenContext::Shared sharedCodeGenContext(&tp, entryPointIndices, sink, endToEndReq); + CodeGenContext codeGenContext(&sharedCodeGenContext); + + ComPtr<IArtifact> outArtifact; + SlangResult res = codeGenContext.emitTranslationUnit(outArtifact); + if (res != SLANG_OK) + { + return res; + } + + ISlangBlob* blob; + outArtifact->loadBlob(ArtifactKeep::Yes, &blob); + + auto builder = IRBuilder(module); + builder.setInsertInto(module); + + switch (targetReq->getTarget()) + { + case CodeGenTarget::DXIL: + builder.emitEmbeddedDXIL(blob); + break; + default: + assert(!"Unhandled target"); + break; + } + + return SLANG_OK; + } +} diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index 912e42572..1ef17df1d 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -762,6 +762,11 @@ namespace Slang # pragma warning(pop) #endif + SlangResult CodeGenContext::emitTranslationUnit(ComPtr<IArtifact>& outArtifact) + { + return emitWithDownstreamForEntryPoints(outArtifact); + } + String GetHLSLProfileName(Profile profile) { switch( profile.getFamily() ) @@ -1084,6 +1089,23 @@ namespace Slang return SLANG_OK; } + bool CodeGenContext::isPrecompiled() + { + auto program = getProgram(); + + bool allPrecompiled = true; + program->enumerateIRModules([&](IRModule* irModule) + { + // TODO: Conditionalize this on target + if (!irModule->precompiledDXIL) + { + allPrecompiled = false; + } + }); + + return allPrecompiled; + } + SlangResult CodeGenContext::emitWithDownstreamForEntryPoints(ComPtr<IArtifact>& outArtifact) { outArtifact.setNull(); @@ -1245,12 +1267,15 @@ namespace Slang } else { - CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker); + if (!isPrecompiled()) + { + CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker); - SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(sourceArtifact)); - sourceCodeGenContext.maybeDumpIntermediate(sourceArtifact); + SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(sourceArtifact)); + sourceCodeGenContext.maybeDumpIntermediate(sourceArtifact); - sourceLanguage = (SourceLanguage)TypeConvertUtil::getSourceLanguageFromTarget((SlangCompileTarget)sourceTarget); + sourceLanguage = (SourceLanguage)TypeConvertUtil::getSourceLanguageFromTarget((SlangCompileTarget)sourceTarget); + } } if (sourceArtifact) @@ -1541,6 +1566,25 @@ namespace Slang libraries.addRange(linkage->m_libModules.getBuffer(), linkage->m_libModules.getCount()); } + if (isPrecompiled()) + { + auto program = getProgram(); + program->enumerateIRModules([&](IRModule* irModule) + { + // TODO: conditionalize on target + if (irModule->precompiledDXIL) + { + ArtifactDesc desc = ArtifactDescUtil::makeDescForCompileTarget(SLANG_DXIL); + desc.kind = ArtifactKind::Library; + + auto library = ArtifactUtil::createArtifact(desc); + + library->addRepresentationUnknown(irModule->precompiledDXIL); + libraries.add(library); + } + }); + } + options.compilerSpecificArguments = allocator.allocate(compilerSpecificArguments); options.requiredCapabilityVersions = SliceUtil::asSlice(requiredCapabilityVersions); options.libraries = SliceUtil::asSlice(libraries); @@ -1991,7 +2035,10 @@ namespace Slang { if (auto artifact = targetProgram->getExistingWholeProgramResult()) { - artifacts.add(ComPtr<IArtifact>(artifact)); + if (!targetProgram->getOptionSet().getBoolOption(CompilerOptionName::EmbedDXIL)) + { + artifacts.add(ComPtr<IArtifact>(artifact)); + } } } else @@ -2251,7 +2298,7 @@ namespace Slang void EndToEndCompileRequest::generateOutput() { generateOutput(getSpecializedGlobalAndEntryPointsComponentType()); - + // If we are in command-line mode, we might be expected to actually // write output to one or more files here. diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 4bae6c10d..8e1cb0a88 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -1475,6 +1475,12 @@ namespace Slang virtual SLANG_NO_THROW char const* SLANG_MCALL getDependencyFilePath( SlangInt32 index) override; + /// Precompile TU to target language + virtual SLANG_NO_THROW SlangResult SLANG_MCALL precompileForTargets( + DiagnosticSink* sink, + EndToEndCompileRequest* endToEndReq, + TargetRequest* targetReq); + virtual void buildHash(DigestBuilder<SHA1>& builder) SLANG_OVERRIDE; virtual slang::DeclReflection* getModuleReflection() SLANG_OVERRIDE; @@ -2714,6 +2720,8 @@ namespace Slang SlangResult emitEntryPoints(ComPtr<IArtifact>& outArtifact); + SlangResult emitTranslationUnit(ComPtr<IArtifact>& outArtifact); + void maybeDumpIntermediate(IArtifact* artifact); protected: @@ -2753,6 +2761,10 @@ namespace Slang SlangResult _emitEntryPoints(ComPtr<IArtifact>& outArtifact); + /* Checks if all modules in the target program are already compiled to the + target language, indicating that a pass-through linking using the + downstream compiler is viable.*/ + bool isPrecompiled(); private: Shared* m_shared = nullptr; }; @@ -2790,6 +2802,8 @@ namespace Slang virtual SLANG_NO_THROW void SLANG_MCALL setTargetFloatingPointMode(int targetIndex, SlangFloatingPointMode mode) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setTargetMatrixLayoutMode(int targetIndex, SlangMatrixLayoutMode mode) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setTargetForceGLSLScalarBufferLayout(int targetIndex, bool value) SLANG_OVERRIDE; + virtual SLANG_NO_THROW void SLANG_MCALL setTargetGenerateWholeProgram(int targetIndex, bool value) SLANG_OVERRIDE; + virtual SLANG_NO_THROW void SLANG_MCALL setTargetEmbedDXIL(int targetIndex, bool value) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setMatrixLayoutMode(SlangMatrixLayoutMode mode) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setDebugInfoLevel(SlangDebugInfoLevel level) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setOptimizationLevel(SlangOptimizationLevel level) SLANG_OVERRIDE; diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 487f264d5..6d7be3f92 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -396,6 +396,9 @@ DIAGNOSTIC(36110, Error, stageIsIncompatibleWithCapabilityDefinition, "'$0' is d DIAGNOSTIC(36111, Error, unexpectedCapability, "'$0' resolves into a disallowed `$1` Capability.") DIAGNOSTIC(36112, Warning, entryPointAndProfileAreIncompatible, "'$0' is defined for stage '$1', which is incompatible with the declared profile '$2'.") DIAGNOSTIC(36113, Warning, usingInternalCapabilityName, "'$0' resolves into a '_Internal' `_$1' Capability, use '$1' instead.") +DIAGNOSTIC(36114, Warning, incompatibleWithPrecompileLib, "Precompiled library requires '$0', has `$1`, implicitly upgrading capabilities.") +DIAGNOSTIC(36115, Error, incompatibleWithPrecompileLibRestrictive, "Precompiled library requires '$0', has `$1`.") + // Attributes DIAGNOSTIC(31000, Warning, unknownAttributeName, "unknown attribute '$0'") diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 07694b066..0e84be2b2 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -294,6 +294,7 @@ INST(Block, block, 0, PARENT) INST(FloatLit, float_constant, 0, 0) INST(PtrLit, ptr_constant, 0, 0) INST(StringLit, string_constant, 0, 0) + INST(BlobLit, string_constant, 0, 0) INST(VoidLit, void_constant, 0, 0) INST_RANGE(Constant, BoolLit, VoidLit) @@ -1205,6 +1206,9 @@ INST(DebugLine, DebugLine, 5, 0) INST(DebugVar, DebugVar, 4, 0) INST(DebugValue, DebugValue, 2, 0) +/* Embedded Precompiled Libraries */ +INST(EmbeddedDXIL, EmbeddedDXIL, 1, 0) + /* Inline assembly */ INST(SPIRVAsm, SPIRVAsm, 0, PARENT) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 795a79c28..8a801e4e7 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -3431,6 +3431,7 @@ public: IRInst* getIntValue(IRType* type, IRIntegerValue value); IRInst* getFloatValue(IRType* type, IRFloatingPointValue value); IRStringLit* getStringValue(const UnownedStringSlice& slice); + IRBlobLit* getBlobValue(ISlangBlob* blob); IRPtrLit* _getPtrValue(void* ptr); IRPtrLit* getNullPtrValue(IRType* type); IRPtrLit* getNullVoidPtrValue() { return getNullPtrValue(getPtrType(getVoidType())); } @@ -3947,6 +3948,8 @@ public: IRInst* emitByteAddressBufferStore(IRInst* byteAddressBuffer, IRInst* offset, IRInst* value); IRInst* emitByteAddressBufferStore(IRInst* byteAddressBuffer, IRInst* offset, IRInst* alignment, IRInst* value); + IRInst* emitEmbeddedDXIL(ISlangBlob* blob); + IRFunc* createFunc(); IRGlobalVar* createGlobalVar( IRType* valueType); diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 1fc15f185..bfd6c20cf 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -2059,7 +2059,7 @@ namespace Slang UnownedStringSlice IRConstant::getStringSlice() { - assert(getOp() == kIROp_StringLit); + assert(getOp() == kIROp_StringLit || getOp() == kIROp_BlobLit); // If the transitory decoration is set, then this is uses the transitoryStringVal for the text storage. // This is typically used when we are using a transitory IRInst held on the stack (such that it can be looked up in cached), // that just points to a string elsewhere, and NOT the typical normal style, where the string is held after the instruction in memory. @@ -2133,6 +2133,7 @@ namespace Slang { return value.ptrVal == rhs->value.ptrVal; } + case kIROp_BlobLit: case kIROp_StringLit: { return getStringSlice() == rhs->getStringSlice(); @@ -2174,6 +2175,7 @@ namespace Slang { return combineHash(code, Slang::getHashCode(value.ptrVal)); } + case kIROp_BlobLit: case kIROp_StringLit: { const UnownedStringSlice slice = getStringSlice(); @@ -2264,6 +2266,7 @@ namespace Slang irValue->value.ptrVal = keyInst.value.ptrVal; break; } + case kIROp_BlobLit: case kIROp_StringLit: { const UnownedStringSlice slice = keyInst.getStringSlice(); @@ -2387,6 +2390,36 @@ namespace Slang return static_cast<IRStringLit*>(_findOrEmitConstant(keyInst)); } + IRBlobLit* IRBuilder::getBlobValue(ISlangBlob* blob) + { + IRConstant keyInst; + memset(&keyInst, 0, sizeof(keyInst)); + + char* buffer = (char*)(getModule()->getMemoryArena().allocate(blob->getBufferSize())); + if (!buffer) + { + return nullptr; + } + memcpy(buffer, blob->getBufferPointer(), blob->getBufferSize()); + + UnownedStringSlice inSlice(buffer, blob->getBufferSize()); + + // Mark that this is on the stack... + IRDecoration stackDecoration; + memset(&stackDecoration, 0, sizeof(stackDecoration)); + stackDecoration.m_op = kIROp_TransitoryDecoration; + stackDecoration.insertAtEnd(&keyInst); + + keyInst.m_op = kIROp_BlobLit; + keyInst.typeUse.usedValue = nullptr; // not used + + IRConstant::StringSliceValue& dstSlice = keyInst.value.transitoryStringVal; + dstSlice.chars = const_cast<char*>(inSlice.begin()); + dstSlice.numChars = uint32_t(inSlice.getLength()); + + return static_cast<IRBlobLit*>(_findOrEmitConstant(keyInst)); + } + IRPtrLit* IRBuilder::_getPtrValue(void* data) { auto type = getPtrType(getVoidType()); @@ -3800,6 +3833,13 @@ namespace Slang return nullptr; } + IRInst* IRBuilder::emitEmbeddedDXIL(ISlangBlob *blob) + { + IRInst* args[] = { getBlobValue(blob) }; + + return emitIntrinsicInst(getVoidType(), kIROp_EmbeddedDXIL, 1, args); + } + enum class TypeCastStyle { Unknown = -1, @@ -7098,6 +7138,10 @@ namespace Slang dump(context, irConst->value.intVal ? "true" : "false"); return; + case kIROp_BlobLit: + dump(context, "<binary blob>"); + return; + case kIROp_StringLit: dumpEncodeString(context, irConst->getStringSlice()); return; diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index cb8e83df8..719b383c3 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -1157,6 +1157,11 @@ struct IRStringLit : IRConstant IR_LEAF_ISA(StringLit); }; +struct IRBlobLit : IRConstant +{ + IR_LEAF_ISA(BlobLit); +}; + struct IRPtrLit : IRConstant { IR_LEAF_ISA(PtrLit); @@ -2340,6 +2345,9 @@ public: { return m_containerPool; } + + // TODO: make a map with lookup by target? + ComPtr<ISlangBlob> precompiledDXIL; private: IRModule() = delete; diff --git a/source/slang/slang-module-library.cpp b/source/slang/slang-module-library.cpp index 02ace07d3..42b9ff77a 100644 --- a/source/slang/slang-module-library.cpp +++ b/source/slang/slang-module-library.cpp @@ -83,6 +83,15 @@ SlangResult loadModuleLibrary(const Byte* inBytes, size_t bytesCount, String pat module, &sink); if (!loadedModule) return SLANG_FAIL; + + for (auto inst : module.irModule->getModuleInst()->getChildren()) + { + if (inst->getOp() == kIROp_EmbeddedDXIL) + { + auto slice = static_cast<IRBlobLit*>(inst->getOperand(0))->getStringSlice(); + module.irModule->precompiledDXIL = StringBlob::create(slice); + } + } library->m_modules.add(loadedModule); } } diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index c3a0eeddc..33103442d 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -353,7 +353,9 @@ void initCommandOptions(CommandOptions& options) "The language to be used for source embedding. Defaults to C/C++. Currently only C/C++ are supported"}, { OptionKind::DisableShortCircuit, "-disable-short-circuit", nullptr, "Disable short-circuiting for \"&&\" and \"||\" operations" }, { OptionKind::UnscopedEnum, "-unscoped-enum", nullptr, "Treat enums types as unscoped by default."}, - { OptionKind::PreserveParameters, "-preserve-params", nullptr, "Preserve all resource parameters in the output code, even if they are not used by the shader."} + { OptionKind::PreserveParameters, "-preserve-params", nullptr, "Preserve all resource parameters in the output code, even if they are not used by the shader."}, + { OptionKind::EmbedDXIL, "-embed-dxil", nullptr, + "Embed DXIL into emitted slang-modules for faster linking" }, }; _addOptions(makeConstArrayView(generalOpts), options); @@ -426,7 +428,6 @@ void initCommandOptions(CommandOptions& options) "A path to a specific spirv.core.grammar.json to use when generating SPIR-V output" }, { OptionKind::IncompleteLibrary, "-incomplete-library", nullptr, "Allow generating code from incomplete libraries with unresolved external functions" }, - }; _addOptions(makeConstArrayView(targetOpts), options); @@ -699,6 +700,8 @@ struct OptionsParser void setProfileVersion(RawTarget* rawTarget, ProfileVersion profileVersion); void setProfile(RawTarget* rawTarget, Profile profile); void addCapabilityAtom(RawTarget* rawTarget, CapabilityName atom); + + SlangResult addEmbeddedLibrary(const CodeGenTarget format, CompilerOptionName option); void setFloatingPointMode(RawTarget* rawTarget, FloatingPointMode mode); @@ -1636,6 +1639,23 @@ SlangResult OptionsParser::_parseProfile(const CommandLineArg& arg) return SLANG_OK; } +// Creates a target of the specified type whose output will be embedded as IR metadata +SlangResult OptionsParser::addEmbeddedLibrary(const CodeGenTarget format, CompilerOptionName option) +{ + RawTarget rawTarget; + rawTarget.format = format; + // Silently allow redundant targets if it is the same as the last specified target. + if (m_rawTargets.getCount() == 0 || m_rawTargets.getLast().format != rawTarget.format) + { + m_rawTargets.add(rawTarget); + } + + getCurrentTarget()->optionSet.add(option, true); + getCurrentTarget()->optionSet.add(CompilerOptionName::GenerateWholeProgram, true); + + return SLANG_OK; +} + SlangResult OptionsParser::_parse( int argc, char const* const* argv) @@ -1927,6 +1947,7 @@ SlangResult OptionsParser::_parse( linkage->m_optionSet.set(optionKind, compressionType); break; } + case OptionKind::EmbedDXIL: SLANG_RETURN_ON_FAIL(addEmbeddedLibrary(CodeGenTarget::DXIL, CompilerOptionName::EmbedDXIL)); break; case OptionKind::Target: { CommandLineArg name; @@ -2758,6 +2779,16 @@ SlangResult OptionsParser::_parse( { m_compileRequest->setTargetForceGLSLScalarBufferLayout(targetID, true); } + + if (rawTarget.optionSet.getBoolOption(CompilerOptionName::GenerateWholeProgram)) + { + m_compileRequest->setTargetGenerateWholeProgram(targetID, true); + } + + if (rawTarget.optionSet.getBoolOption(CompilerOptionName::EmbedDXIL)) + { + m_compileRequest->setTargetEmbedDXIL(targetID, true); + } } // Next we need to sort out the output files specified with `-o`, and diff --git a/source/slang/slang-serialize-container.cpp b/source/slang/slang-serialize-container.cpp index 50b9061e3..2229c74ea 100644 --- a/source/slang/slang-serialize-container.cpp +++ b/source/slang/slang-serialize-container.cpp @@ -348,6 +348,12 @@ namespace Slang { } } + // TODO: + // Serialization of target component IR is causing the embedded precompiled binary + // feature to fail. The resulting data modules contain both TU IR and TC IR, with only + // one module header. Yong suggested to ignore the TC IR for now, though also that + // OV was using the feature, so disabling this might cause problems. +#if 0 if (data.targetComponents.getCount() && (options.optionFlags & SerialOptionFlag::IRModule)) { // TODO: in the case where we have specialization, we might need @@ -365,6 +371,7 @@ namespace Slang { SLANG_RETURN_ON_FAIL(IRSerialWriter::writeContainer(serialData, options.compressionType, container)); } } +#endif } if (data.entryPoints.getCount()) diff --git a/source/slang/slang-serialize-ir.cpp b/source/slang/slang-serialize-ir.cpp index ccd5a2e7d..a2d05fc5b 100644 --- a/source/slang/slang-serialize-ir.cpp +++ b/source/slang/slang-serialize-ir.cpp @@ -195,6 +195,14 @@ Result IRSerialWriter::write(IRModule* module, SerialSourceLocWriter* sourceLocW switch (srcInst->getOp()) { // Special handling for the ir const derived types + case kIROp_BlobLit: + { + // Blobs are serialized into string table like strings + auto stringLit = static_cast<IRBlobLit*>(srcInst); + dstInst.m_payloadType = PayloadType::String_1; + dstInst.m_payload.m_stringIndices[0] = getStringIndex(stringLit->getStringSlice()); + break; + } case kIROp_StringLit: { auto stringLit = static_cast<IRStringLit*>(srcInst); @@ -790,6 +798,7 @@ Result IRSerialReader::read(const IRSerialData& data, Session* session, SerialSo op, operandCount, prefixSize)); break; } + case kIROp_BlobLit: case kIROp_StringLit: { SLANG_ASSERT(srcInst.m_payloadType == PayloadType::String_1); diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 6f5e79458..cf8c19033 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -3149,6 +3149,24 @@ SlangResult EndToEndCompileRequest::executeActionsInner() return SLANG_OK; } + // If requested, attempt to compile the translation unit all the way down to the target language + // and stash the result blob in an IR op. + for (auto targetReq : getLinkage()->targets) + { + if (targetReq->getOptionSet().getBoolOption(CompilerOptionName::EmbedDXIL)) + { + auto frontEndReq = getFrontEndReq(); + + for (auto translationUnit : frontEndReq->translationUnits) + { + translationUnit->getModule()->precompileForTargets( + getSink(), + this, + targetReq); + } + } + } + // If codegen is enabled, we need to move along to // apply any generic specialization that the user asked for. // @@ -5758,6 +5776,16 @@ void EndToEndCompileRequest::setTargetMatrixLayoutMode(int targetIndex, SlangMat getTargetOptionSet(targetIndex).setMatrixLayoutMode(MatrixLayoutMode(mode)); } +void EndToEndCompileRequest::setTargetGenerateWholeProgram(int targetIndex, bool value) +{ + getTargetOptionSet(targetIndex).set(CompilerOptionName::GenerateWholeProgram, value); +} + +void EndToEndCompileRequest::setTargetEmbedDXIL(int targetIndex, bool value) +{ + getTargetOptionSet(targetIndex).set(CompilerOptionName::EmbedDXIL, value); +} + void EndToEndCompileRequest::setTargetLineDirectiveMode( SlangInt targetIndex, SlangLineDirectiveMode mode) |
