summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorcheneym2 <acheney@nvidia.com>2024-08-05 15:37:46 -0400
committerGitHub <noreply@github.com>2024-08-05 15:37:46 -0400
commitd72f9f6f72a7a74d7466a1e301e1853fea5daa25 (patch)
tree4ae77e2dd622779b64d063d1f50fc7af8c13a94a /source/slang
parentd63f5e20f1edf7c51ca5c456baceb9eb9a84c95b (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.capdef2
-rw-r--r--source/slang/slang-compiler-tu.cpp115
-rw-r--r--source/slang/slang-compiler.cpp59
-rwxr-xr-xsource/slang/slang-compiler.h14
-rw-r--r--source/slang/slang-diagnostic-defs.h3
-rw-r--r--source/slang/slang-ir-inst-defs.h4
-rw-r--r--source/slang/slang-ir-insts.h3
-rw-r--r--source/slang/slang-ir.cpp46
-rw-r--r--source/slang/slang-ir.h8
-rw-r--r--source/slang/slang-module-library.cpp9
-rw-r--r--source/slang/slang-options.cpp35
-rw-r--r--source/slang/slang-serialize-container.cpp7
-rw-r--r--source/slang/slang-serialize-ir.cpp9
-rw-r--r--source/slang/slang.cpp28
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)