Skip to content

Commit d72f9f6

Browse files
authored
Initial support for precompiled DXIL in slang-modules (shader-slang#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 shader-slang#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
1 parent d63f5e2 commit d72f9f6

17 files changed

+430
-57
lines changed

include/slang.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,7 @@ extern "C"
970970
UseUpToDateBinaryModule, // bool, when set, will only load
971971
// precompiled modules if it is up-to-date with its source.
972972

973+
EmbedDXIL, // bool
973974
CountOf,
974975
};
975976

@@ -4152,7 +4153,6 @@ namespace slang
41524153
int targetIndex,
41534154
SlangTargetFlags flags) = 0;
41544155

4155-
41564156
/*!
41574157
@brief Set the floating point mode (e.g., precise or fast) to use a target.
41584158
*/
@@ -4704,6 +4704,13 @@ namespace slang
47044704
// return a copy of internal profiling results, and if `shouldClear` is true, clear the internal profiling results before returning.
47054705
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getCompileTimeProfile(ISlangProfiler** compileTimeProfile, bool shouldClear) = 0;
47064706

4707+
virtual SLANG_NO_THROW void SLANG_MCALL setTargetGenerateWholeProgram(
4708+
int targetIndex,
4709+
bool value) = 0;
4710+
4711+
virtual SLANG_NO_THROW void SLANG_MCALL setTargetEmbedDXIL(
4712+
int targetIndex,
4713+
bool value) = 0;
47074714
};
47084715

47094716
#define SLANG_UUID_ICompileRequest ICompileRequest::getTypeGuid()

source/compiler-core/slang-dxc-compiler.cpp

+65-47
Original file line numberDiff line numberDiff line change
@@ -381,18 +381,24 @@ SlangResult DXCDownstreamCompiler::compile(const CompileOptions& inOptions, IArt
381381

382382
CompileOptions options = getCompatibleVersion(&inOptions);
383383

384-
// This compiler can only deal with a single artifact
385-
if (options.sourceArtifacts.count != 1)
384+
// This compiler can only deal at most, a single source code artifact
385+
// Should be okay to link together multiple libraries without any source artifacts (assuming that means source code)
386+
if (options.sourceArtifacts.count > 1)
386387
{
387388
return SLANG_FAIL;
388389
}
389390

390-
IArtifact* sourceArtifact = options.sourceArtifacts[0];
391+
bool hasSource = options.sourceArtifacts.count > 0;
391392

392-
if (options.sourceLanguage != SLANG_SOURCE_LANGUAGE_HLSL || options.targetType != SLANG_DXIL)
393+
IArtifact* sourceArtifact = hasSource ? options.sourceArtifacts[0] : nullptr;
394+
395+
if (hasSource)
393396
{
394-
SLANG_ASSERT(!"Can only compile HLSL to DXIL");
395-
return SLANG_FAIL;
397+
if (options.sourceLanguage != SLANG_SOURCE_LANGUAGE_HLSL || options.targetType != SLANG_DXIL)
398+
{
399+
SLANG_ASSERT(!"Can only compile HLSL to DXIL");
400+
return SLANG_FAIL;
401+
}
396402
}
397403

398404
// Find all of the libraries
@@ -416,16 +422,19 @@ SlangResult DXCDownstreamCompiler::compile(const CompileOptions& inOptions, IArt
416422
ComPtr<IDxcLibrary> dxcLibrary;
417423
SLANG_RETURN_ON_FAIL(m_createInstance(CLSID_DxcLibrary, __uuidof(dxcLibrary), (LPVOID*)dxcLibrary.writeRef()));
418424

425+
ComPtr<IDxcBlobEncoding> dxcSourceBlob = nullptr;
419426
ComPtr<ISlangBlob> sourceBlob;
420-
SLANG_RETURN_ON_FAIL(sourceArtifact->loadBlob(ArtifactKeep::Yes, sourceBlob.writeRef()));
427+
if (hasSource)
428+
{
429+
SLANG_RETURN_ON_FAIL(sourceArtifact->loadBlob(ArtifactKeep::Yes, sourceBlob.writeRef()));
421430

422-
// Create blob from the string
423-
ComPtr<IDxcBlobEncoding> dxcSourceBlob;
424-
SLANG_RETURN_ON_FAIL(dxcLibrary->CreateBlobWithEncodingFromPinned(
425-
(LPBYTE)sourceBlob->getBufferPointer(),
426-
(UINT32)sourceBlob->getBufferSize(),
427-
0,
428-
dxcSourceBlob.writeRef()));
431+
// Create blob from the string
432+
SLANG_RETURN_ON_FAIL(dxcLibrary->CreateBlobWithEncodingFromPinned(
433+
(LPBYTE)sourceBlob->getBufferPointer(),
434+
(UINT32)sourceBlob->getBufferSize(),
435+
0,
436+
dxcSourceBlob.writeRef()));
437+
}
429438

430439
List<const WCHAR*> args;
431440

@@ -508,7 +517,7 @@ SlangResult DXCDownstreamCompiler::compile(const CompileOptions& inOptions, IArt
508517

509518
String profileName = asString(options.profileName);
510519
// If we are going to link we have to compile in the lib profile style
511-
if (libraries.getCount())
520+
if (libraries.getCount() && hasSource)
512521
{
513522
if (!profileName.startsWith("lib"))
514523
{
@@ -561,28 +570,33 @@ SlangResult DXCDownstreamCompiler::compile(const CompileOptions& inOptions, IArt
561570
}
562571
#endif
563572

564-
String sourcePath = ArtifactUtil::findPath(sourceArtifact);
565-
OSString wideSourcePath = sourcePath.toWString();
573+
String sourcePath;
574+
ComPtr<IDxcBlob> dxcResultBlob = nullptr;
575+
auto diagnostics = ArtifactDiagnostics::create();
576+
ComPtr<IDxcOperationResult> dxcOperationResult = nullptr;
577+
if (hasSource)
578+
{
579+
sourcePath = ArtifactUtil::findPath(sourceArtifact);
580+
OSString wideSourcePath = sourcePath.toWString();
566581

567-
DxcIncludeHandler includeHandler(&searchDirectories, options.fileSystemExt, options.sourceManager);
582+
DxcIncludeHandler includeHandler(&searchDirectories, options.fileSystemExt, options.sourceManager);
568583

569-
ComPtr<IDxcOperationResult> dxcOperationResult;
570-
SLANG_RETURN_ON_FAIL(dxcCompiler->Compile(dxcSourceBlob,
571-
wideSourcePath.begin(),
572-
wideEntryPointName.begin(),
573-
wideProfileName.begin(),
574-
args.getBuffer(),
575-
UINT32(args.getCount()),
576-
nullptr, // `#define`s
577-
0, // `#define` count
578-
&includeHandler, // `#include` handler
579-
dxcOperationResult.writeRef()));
584+
SLANG_RETURN_ON_FAIL(dxcCompiler->Compile(dxcSourceBlob,
585+
wideSourcePath.begin(),
586+
wideEntryPointName.begin(),
587+
wideProfileName.begin(),
588+
args.getBuffer(),
589+
UINT32(args.getCount()),
590+
nullptr, // `#define`s
591+
0, // `#define` count
592+
&includeHandler, // `#include` handler
593+
dxcOperationResult.writeRef()));
580594

581-
auto diagnostics = ArtifactDiagnostics::create();
582-
583-
ComPtr<IDxcBlob> dxcResultBlob;
595+
SLANG_RETURN_ON_FAIL(_handleOperationResult(dxcOperationResult, diagnostics, dxcResultBlob));
584596

585-
SLANG_RETURN_ON_FAIL(_handleOperationResult(dxcOperationResult, diagnostics, dxcResultBlob));
597+
ComPtr<IDxcBlobEncoding> dxcResultBlob2 = nullptr;
598+
dxcCompiler->Disassemble(dxcResultBlob, dxcResultBlob2.writeRef());
599+
}
586600

587601
// If we have libraries then we need to link...
588602
if (libraries.getCount())
@@ -604,26 +618,30 @@ SlangResult DXCDownstreamCompiler::compile(const CompileOptions& inOptions, IArt
604618
libraryNames.add(String(_addName(library, pool)).toWString());
605619
}
606620

607-
// Add the compiled blob name
608-
String name;
609-
if (options.modulePath.count)
610-
{
611-
name = Path::getFileNameWithoutExt(asString(options.modulePath));
612-
}
613-
else if (sourcePath.getLength())
621+
if (hasSource)
614622
{
615-
name = Path::getFileNameWithoutExt(sourcePath);
616-
}
623+
// Add the compiled blob name
624+
String name;
625+
if (options.modulePath.count)
626+
{
627+
name = Path::getFileNameWithoutExt(asString(options.modulePath));
628+
}
629+
else if (sourcePath.getLength())
630+
{
631+
name = Path::getFileNameWithoutExt(sourcePath);
632+
}
617633

618-
// Add the blob with name
619-
{
620-
auto blob = (ISlangBlob*)dxcResultBlob.get();
621-
libraryBlobs.add(ComPtr<ISlangBlob>(blob));
622-
libraryNames.add(String(_addName(name.getUnownedSlice(), pool)).toWString());
634+
// Add the blob with name
635+
{
636+
auto blob = (ISlangBlob*)dxcResultBlob.get();
637+
libraryBlobs.add(ComPtr<ISlangBlob>(blob));
638+
libraryNames.add(String(_addName(name.getUnownedSlice(), pool)).toWString());
639+
}
623640
}
624641

625642
const Index librariesCount = libraryNames.getCount();
626643
SLANG_ASSERT(libraryBlobs.getCount() == librariesCount);
644+
SLANG_ASSERT(libraryNames.getCount() == librariesCount);
627645

628646
List<const wchar_t*> linkLibraryNames;
629647

source/slang/slang-capabilities.capdef

+2
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ def _sm_6_7 : _sm_6_6;
119119

120120
def hlsl_nvapi : hlsl;
121121

122+
alias dxil_lib = _sm_6_3;
123+
122124
// cuda versions
123125
def _cuda_sm_1_0 : cuda;
124126
def _cuda_sm_2_0 : _cuda_sm_1_0;

source/slang/slang-compiler-tu.cpp

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// slang-compiler-tu.cpp: Compiles translation units to target language
2+
// and emit precompiled blobs into IR
3+
4+
#include "../core/slang-basic.h"
5+
#include "slang-compiler.h"
6+
#include "slang-ir-insts.h"
7+
#include "slang-capability.h"
8+
9+
namespace Slang
10+
{
11+
SLANG_NO_THROW SlangResult SLANG_MCALL Module::precompileForTargets(
12+
DiagnosticSink* sink,
13+
EndToEndCompileRequest* endToEndReq,
14+
TargetRequest* targetReq)
15+
{
16+
auto module = getIRModule();
17+
Slang::Session* session = endToEndReq->getSession();
18+
Slang::ASTBuilder* astBuilder = session->getGlobalASTBuilder();
19+
Slang::Linkage* builtinLinkage = session->getBuiltinLinkage();
20+
Slang::Linkage linkage(session, astBuilder, builtinLinkage);
21+
22+
CapabilityName precompileRequirement = CapabilityName::Invalid;
23+
switch (targetReq->getTarget())
24+
{
25+
case CodeGenTarget::DXIL:
26+
linkage.addTarget(Slang::CodeGenTarget::DXIL);
27+
precompileRequirement = CapabilityName::dxil_lib;
28+
break;
29+
default:
30+
assert(!"Unhandled target");
31+
break;
32+
}
33+
SLANG_ASSERT(precompileRequirement != CapabilityName::Invalid);
34+
35+
// Ensure precompilation capability requirements are met.
36+
auto targetCaps = targetReq->getTargetCaps();
37+
auto precompileRequirementsCapabilitySet = CapabilitySet(precompileRequirement);
38+
if (targetCaps.atLeastOneSetImpliedInOther(precompileRequirementsCapabilitySet) == CapabilitySet::ImpliesReturnFlags::NotImplied)
39+
{
40+
// If `RestrictiveCapabilityCheck` is true we will error, else we will warn.
41+
// error ...: dxil libraries require $0, entry point compiled with $1.
42+
// warn ...: dxil libraries require $0, entry point compiled with $1, implicitly upgrading capabilities.
43+
maybeDiagnoseWarningOrError(
44+
sink,
45+
targetReq->getOptionSet(),
46+
DiagnosticCategory::Capability,
47+
SourceLoc(),
48+
Diagnostics::incompatibleWithPrecompileLib,
49+
Diagnostics::incompatibleWithPrecompileLibRestrictive,
50+
precompileRequirementsCapabilitySet,
51+
targetCaps);
52+
53+
// add precompile requirements to the cooked targetCaps
54+
targetCaps.join(precompileRequirementsCapabilitySet);
55+
if (targetCaps.isInvalid())
56+
{
57+
sink->diagnose(SourceLoc(), Diagnostics::unknownCapability, targetCaps);
58+
return SLANG_FAIL;
59+
}
60+
else
61+
{
62+
targetReq->setTargetCaps(targetCaps);
63+
}
64+
}
65+
66+
List<RefPtr<ComponentType>> allComponentTypes;
67+
allComponentTypes.add(this); // Add Module as a component type
68+
69+
for (auto entryPoint : this->getEntryPoints())
70+
{
71+
allComponentTypes.add(entryPoint); // Add the entry point as a component type
72+
}
73+
74+
auto composite = CompositeComponentType::create(
75+
&linkage,
76+
allComponentTypes);
77+
78+
TargetProgram tp(composite, targetReq);
79+
tp.getOrCreateLayout(sink);
80+
Slang::Index const entryPointCount = m_entryPoints.getCount();
81+
82+
CodeGenContext::EntryPointIndices entryPointIndices;
83+
84+
entryPointIndices.setCount(entryPointCount);
85+
for (Index i = 0; i < entryPointCount; i++)
86+
entryPointIndices[i] = i;
87+
CodeGenContext::Shared sharedCodeGenContext(&tp, entryPointIndices, sink, endToEndReq);
88+
CodeGenContext codeGenContext(&sharedCodeGenContext);
89+
90+
ComPtr<IArtifact> outArtifact;
91+
SlangResult res = codeGenContext.emitTranslationUnit(outArtifact);
92+
if (res != SLANG_OK)
93+
{
94+
return res;
95+
}
96+
97+
ISlangBlob* blob;
98+
outArtifact->loadBlob(ArtifactKeep::Yes, &blob);
99+
100+
auto builder = IRBuilder(module);
101+
builder.setInsertInto(module);
102+
103+
switch (targetReq->getTarget())
104+
{
105+
case CodeGenTarget::DXIL:
106+
builder.emitEmbeddedDXIL(blob);
107+
break;
108+
default:
109+
assert(!"Unhandled target");
110+
break;
111+
}
112+
113+
return SLANG_OK;
114+
}
115+
}

0 commit comments

Comments
 (0)