From ddf4a323be5ae4e59dce742f618dbbdee4ed28d8 Mon Sep 17 00:00:00 2001 From: cheneym2 Date: Fri, 30 Aug 2024 02:23:26 -0400 Subject: [PATCH] Support mixture of precompiled and non-precompiled modules (#4860) * Support mixture of precompiled and non-precompiled modules This changes the implementation of precompile DXIL modules to accept combinations of modules with precompiled DXIL, ones without, and ones with a mixture of precompiled DXIL and Slang IR. During precompilation, module IR is analyzed to find public functions which appear to be capable of being compiled as HLSL, and those functions are given a HLSLExport decoration, ensuring they are emitted as HLSL and preserved in the precompiled DXIL blob. The IR for those functions is then tagged with a new decoration AvailableInDXIL, which marks that their implementation is present in the embedded DXIL blob. The DXIL blob is attached to the IR as before, inside a EmbeddedDXIL BlobLit instruction. The logic that determines whether or not functions should be precompiled to DXIL is a placeholder at this point, returning true always. A subsequent change will add selection criteria. During module linking, the full module IR is available, as well as the optional EmbeddedDXIL blob. The IR for functions implemented by the blob are tagged with AvailableInDXIL in the module IR. After linking the IR for all modules to program level IR, the IR for the functions marked AvailableInDXIL are deleted from the linked IR, prior to emitting HLSL and compiling linking the result. This change also changes the point of time when the module IR is checked for EmbeddedDXIL blobs. Instead of happening at load time as before, it happens during immediately before final linking, meaning that the blob does not need to be independently stored with the module separate from the IR as was done previously. Work on #4792 * Clean up debug prints * Call isSimpleHLSLDataType stub * Address feedback on precompiled dxil support Allow for IR filtering both before and after linking. Only mark AvailableInDXIL those functions which pass both filtering stages. Functions are corrlated using mangled function names. Rather than delete functions entirely when linking with libraries that include precompiled DXIL, instead convert the IR function definitions to declarations by gutting them, removing child blocks. * Use artifact metadata and name list instead of linkedir hack * Use String instead of UnownedStringSlice * Update tests * Renaming * Minor edits * Don't fully remove functions post-link * Unexport before collecting metadata --- include/slang.h | 4 +- .../slang-artifact-associated-impl.cpp | 5 + .../slang-artifact-associated-impl.h | 4 +- .../compiler-core/slang-artifact-associated.h | 3 + source/slang/slang-compiler-tu.cpp | 138 +++++++++++++++++- source/slang/slang-compiler.cpp | 56 +++---- source/slang/slang-compiler.h | 14 +- source/slang/slang-emit.cpp | 44 ++++++ source/slang/slang-ir-inst-defs.h | 3 + source/slang/slang-ir-insts.h | 2 + source/slang/slang-ir-link.cpp | 2 +- source/slang/slang-ir-metadata.cpp | 10 ++ source/slang/slang-ir-redundancy-removal.cpp | 30 ++++ source/slang/slang-ir-redundancy-removal.h | 2 + source/slang/slang-ir-util.cpp | 8 + source/slang/slang-ir-util.h | 2 + source/slang/slang-ir.h | 3 - source/slang/slang-module-library.cpp | 9 -- source/slang/slang-options.cpp | 26 +--- source/slang/slang.cpp | 25 ++-- tests/library/export-library-generics.slang | 39 +++++ tests/library/module-library-matrix.slang | 8 + tests/library/precompiled-dxil-generics.slang | 28 ++++ tests/library/precompiled-dxil-matrix.slang | 26 ++++ tests/library/precompiled-dxil.slang | 3 +- .../precompiled-module-library-resource.slang | 33 +++++ 26 files changed, 425 insertions(+), 102 deletions(-) create mode 100644 tests/library/export-library-generics.slang create mode 100644 tests/library/module-library-matrix.slang create mode 100644 tests/library/precompiled-dxil-generics.slang create mode 100644 tests/library/precompiled-dxil-matrix.slang create mode 100644 tests/library/precompiled-module-library-resource.slang diff --git a/include/slang.h b/include/slang.h index 7c417e74e0..ed29665925 100644 --- a/include/slang.h +++ b/include/slang.h @@ -4929,9 +4929,7 @@ namespace slang int targetIndex, bool value) = 0; - virtual SLANG_NO_THROW void SLANG_MCALL setTargetEmbedDXIL( - int targetIndex, - bool value) = 0; + virtual SLANG_NO_THROW void SLANG_MCALL setEmbedDXIL(bool value) = 0; virtual SLANG_NO_THROW void SLANG_MCALL setTargetForceDXLayout(int targetIndex, bool value) = 0; }; diff --git a/source/compiler-core/slang-artifact-associated-impl.cpp b/source/compiler-core/slang-artifact-associated-impl.cpp index f6f64e294d..f29c0f596f 100644 --- a/source/compiler-core/slang-artifact-associated-impl.cpp +++ b/source/compiler-core/slang-artifact-associated-impl.cpp @@ -308,4 +308,9 @@ Slice ArtifactPostEmitMetadata::getUsedBindingRanges() return Slice(m_usedBindings.getBuffer(), m_usedBindings.getCount()); } +Slice ArtifactPostEmitMetadata::getExportedFunctionMangledNames() +{ + return Slice(m_exportedFunctionMangledNames.getBuffer(), m_exportedFunctionMangledNames.getCount()); +} + } // namespace Slang diff --git a/source/compiler-core/slang-artifact-associated-impl.h b/source/compiler-core/slang-artifact-associated-impl.h index 5144fe7a7e..a6e323b0a1 100644 --- a/source/compiler-core/slang-artifact-associated-impl.h +++ b/source/compiler-core/slang-artifact-associated-impl.h @@ -155,13 +155,15 @@ class ArtifactPostEmitMetadata : public ComBaseObject, public IArtifactPostEmitM // IArtifactPostEmitMetadata SLANG_NO_THROW virtual Slice SLANG_MCALL getUsedBindingRanges() SLANG_OVERRIDE; - + SLANG_NO_THROW virtual Slice SLANG_MCALL getExportedFunctionMangledNames() SLANG_OVERRIDE; + void* getInterface(const Guid& uuid); void* getObject(const Guid& uuid); static ComPtr create() { return ComPtr(new ThisType); } List m_usedBindings; + List m_exportedFunctionMangledNames; }; } // namespace Slang diff --git a/source/compiler-core/slang-artifact-associated.h b/source/compiler-core/slang-artifact-associated.h index f63b8a6730..7664942713 100644 --- a/source/compiler-core/slang-artifact-associated.h +++ b/source/compiler-core/slang-artifact-associated.h @@ -124,6 +124,9 @@ class IArtifactPostEmitMetadata : public ICastable /// Get the binding ranges SLANG_NO_THROW virtual Slice SLANG_MCALL getUsedBindingRanges() = 0; + + /// Get the list of functions that were exported in the linked IR + SLANG_NO_THROW virtual Slice SLANG_MCALL getExportedFunctionMangledNames() = 0; }; } // namespace Slang diff --git a/source/slang/slang-compiler-tu.cpp b/source/slang/slang-compiler-tu.cpp index d4f3976a63..fe778148bb 100644 --- a/source/slang/slang-compiler-tu.cpp +++ b/source/slang/slang-compiler-tu.cpp @@ -4,10 +4,91 @@ #include "../core/slang-basic.h" #include "slang-compiler.h" #include "slang-ir-insts.h" +#include "slang-ir-util.h" #include "slang-capability.h" namespace Slang { + // Only attempt to precompile functions: + // 1) With function bodies (not just empty decls) + // 2) Not marked with unsafeForceInlineDecoration + // 3) Have a simple HLSL data type as the return or parameter type + static bool attemptPrecompiledExport(IRInst* inst) + { + if (inst->getOp() != kIROp_Func) + { + return false; + } + + // Skip functions with no body + bool hasBody = false; + for (auto child : inst->getChildren()) + { + if (child->getOp() == kIROp_Block) + { + hasBody = true; + break; + } + } + if (!hasBody) + { + return false; + } + + // Skip functions marked with unsafeForceInlineDecoration + if (inst->findDecoration()) + { + return false; + } + + // Skip non-simple HLSL data types, filters out generics + if (!isSimpleHLSLDataType(inst)) + { + return false; + } + + return true; + } + + /* + * Precompile the module for the given target. + * + * This function creates a target program and emits the precompiled blob as + * an embedded blob in the module IR, e.g. DXIL. + * Because the IR for the Slang Module may violate the restrictions of the + * target language, the emitted target blob may not be able to include the + * full module, but rather only the subset that can be precompiled. For + * example, DXIL libraries do not allow resources like structured buffers + * to appear in the library interface. Also, no target languages allow + * generics to be precompiled. + * + * Some restrictions can be enforced up front before linking, but some are + * done during target generation in between IR linking+legalization and + * target source emission. + * + * Functions which can be rejected up front: + * - Functions with no body + * - Functions marked with unsafeForceInlineDecoration + * - Functions that define or use generics + * + * The functions not rejected up front are marked with + * DownstreamModuleExportDecoration which indicates functions we're trying to + * export for precompilation, and this also helps to identify the functions + * in the linked IR which survived the additional pruning. + * + * Functions that are rejected after linking+legalization (inside + * emitPrecompiled*): + * - (DXIL) Functions that return or take a HLSLStructuredBufferType + * - (DXIL) Functions that return or take a Matrix type + * + * emitPrecompiled* produces the output artifact containing target language + * blob, and as metadata, the list of functions which survived the second + * phase of filtering. + * + * The original module IR functions matching those are then marked with + * "AvailableIn*" (e.g. AvailableInDXILDecoration) to indicate to future + * module users which functions are present in the precompiled blob. + */ SLANG_NO_THROW SlangResult SLANG_MCALL Module::precompileForTarget( SlangCompileTarget target, slang::IBlob** outDiagnostics) @@ -20,6 +101,7 @@ namespace Slang auto module = getIRModule(); auto linkage = getLinkage(); + auto builder = IRBuilder(module); DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer); applySettingsToDiagnosticSink(&sink, &sink, linkage->m_optionSet); @@ -48,6 +130,7 @@ namespace Slang { case CodeGenTarget::DXIL: tp.getOptionSet().add(CompilerOptionName::Profile, Profile::RawEnum::DX_Lib_6_6); + tp.getOptionSet().add(CompilerOptionName::EmbedDXIL, true); break; } @@ -59,20 +142,69 @@ namespace Slang CodeGenContext::Shared sharedCodeGenContext(&tp, entryPointIndices, &sink, nullptr); CodeGenContext codeGenContext(&sharedCodeGenContext); + // Mark all public functions as exported, ensure there's at least one. Store a mapping + // of function name to IRInst* for later reference. After linking is done, we'll scan + // the linked result to see which functions survived the pruning and are included in the + // precompiled blob. + Dictionary nameToFunction; + bool hasAtLeastOneFunction = false; + for (auto inst : module->getGlobalInsts()) + { + if (attemptPrecompiledExport(inst)) + { + hasAtLeastOneFunction = true; + builder.addDecoration(inst, kIROp_DownstreamModuleExportDecoration); + nameToFunction[inst->findDecoration()->getMangledName()] = inst; + } + } + + // Bail if there are no functions to export. That's not treated as an error + // because it's possible that the module just doesn't have any simple HLSL. + if (!hasAtLeastOneFunction) + { + return SLANG_OK; + } + ComPtr outArtifact; - SlangResult res = codeGenContext.emitTranslationUnit(outArtifact); + SlangResult res = codeGenContext.emitPrecompiledDXIL(outArtifact); sink.getBlobIfNeeded(outDiagnostics); - if (res != SLANG_OK) { return res; } + auto metadata = findAssociatedRepresentation(outArtifact); + if (!metadata) + { + return SLANG_E_NOT_AVAILABLE; + } + + for (const auto& mangledName : metadata->getExportedFunctionMangledNames()) + { + auto moduleInst = nameToFunction[mangledName]; + builder.addDecoration(moduleInst, kIROp_AvailableInDXILDecoration); + auto moduleDec = moduleInst->findDecoration(); + moduleDec->removeAndDeallocate(); + } + + // Finally, clean up the transient export decorations left over in the module. These are + // represent functions that were pruned from the IR after linking, before target generation. + for (auto moduleInst : module->getGlobalInsts()) + { + if (moduleInst->getOp() == kIROp_Func) + { + if (auto dec = moduleInst->findDecoration()) + { + dec->removeAndDeallocate(); + } + } + } + ISlangBlob* blob; outArtifact->loadBlob(ArtifactKeep::Yes, &blob); - auto builder = IRBuilder(module); + // Add the precompiled blob to the module builder.setInsertInto(module); switch (targetReq->getTarget()) diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index 428532658c..a5a09204bc 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -350,7 +350,7 @@ namespace Slang Profile Profile::lookUp(UnownedStringSlice const& name) { #define PROFILE(TAG, NAME, STAGE, VERSION) if(name == UnownedTerminatedStringSlice(#NAME)) return Profile::TAG; - #define PROFILE_ALIAS(TAG, DEF, NAME) if(name == UnownedTerminatedStringSlice(#NAME)) return Profile::TAG; + #define PROFILE_ALIAS(TAG, DEF, NAME) if(name == UnownedTerminatedStringSlice(#NAME)) return Profile::TAG; #include "slang-profile-defs.h" return Profile::Unknown; @@ -767,7 +767,7 @@ namespace Slang # pragma warning(pop) #endif - SlangResult CodeGenContext::emitTranslationUnit(ComPtr& outArtifact) + SlangResult CodeGenContext::emitPrecompiledDXIL(ComPtr& outArtifact) { return emitWithDownstreamForEntryPoints(outArtifact); } @@ -1094,23 +1094,6 @@ 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& outArtifact) { outArtifact.setNull(); @@ -1272,15 +1255,17 @@ namespace Slang } else { - if (!isPrecompiled()) + CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker); + + if (target == CodeGenTarget::DXILAssembly || target == CodeGenTarget::DXIL) { - CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker); + sourceCodeGenContext.removeAvailableInDXIL = true; + } - 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) @@ -1571,24 +1556,29 @@ namespace Slang libraries.addRange(linkage->m_libModules.getBuffer(), linkage->m_libModules.getCount()); } - if (isPrecompiled()) + auto program = getProgram(); + + // Load embedded precompiled libraries from IR into library artifacts + program->enumerateIRModules([&](IRModule* irModule) { - auto program = getProgram(); - program->enumerateIRModules([&](IRModule* irModule) + for (auto inst : irModule->getModuleInst()->getChildren()) + { + if (target == CodeGenTarget::DXILAssembly || target == CodeGenTarget::DXIL) { - // TODO: conditionalize on target - if (irModule->precompiledDXIL) + if (inst->getOp() == kIROp_EmbeddedDXIL) { + auto slice = static_cast(inst->getOperand(0))->getStringSlice(); ArtifactDesc desc = ArtifactDescUtil::makeDescForCompileTarget(SLANG_DXIL); desc.kind = ArtifactKind::Library; auto library = ArtifactUtil::createArtifact(desc); - library->addRepresentationUnknown(irModule->precompiledDXIL); + library->addRepresentationUnknown(StringBlob::create(slice)); libraries.add(library); } - }); - } + } + } + }); options.compilerSpecificArguments = allocator.allocate(compilerSpecificArguments); options.requiredCapabilityVersions = SliceUtil::asSlice(requiredCapabilityVersions); diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 9d796f48d6..54f43b382f 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -555,7 +555,6 @@ namespace Slang /// and parsing via Slang reflection, and is not recommended for future APIs to use. /// Scope* _getOrCreateScopeForLegacyLookup(ASTBuilder* astBuilder); - protected: ComponentType(Linkage* linkage); @@ -2732,10 +2731,14 @@ namespace Slang SlangResult emitEntryPoints(ComPtr& outArtifact); - SlangResult emitTranslationUnit(ComPtr& outArtifact); + SlangResult emitPrecompiledDXIL(ComPtr& outArtifact); void maybeDumpIntermediate(IArtifact* artifact); + // Used to cause instructions available in precompiled DXIL to be + // removed between IR linking and target source generation. + bool removeAvailableInDXIL = false; + protected: CodeGenTarget m_targetFormat = CodeGenTarget::Unknown; ExtensionTracker* m_extensionTracker = nullptr; @@ -2772,11 +2775,6 @@ namespace Slang SlangResult _emitEntryPoints(ComPtr& 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; }; @@ -2816,7 +2814,7 @@ namespace Slang virtual SLANG_NO_THROW void SLANG_MCALL setTargetForceGLSLScalarBufferLayout(int targetIndex, bool value) SLANG_OVERRIDE; virtual SLANG_NO_THROW void SLANG_MCALL setTargetForceDXLayout(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 setEmbedDXIL(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-emit.cpp b/source/slang/slang-emit.cpp index 103cd15ab4..caa8ca8eae 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -58,6 +58,7 @@ #include "slang-ir-metadata.h" #include "slang-ir-optix-entry-point-uniforms.h" #include "slang-ir-pytorch-cpp-binding.h" +#include "slang-ir-redundancy-removal.h" #include "slang-ir-restructure.h" #include "slang-ir-restructure-scoping.h" #include "slang-ir-sccp.h" @@ -415,6 +416,37 @@ bool checkStaticAssert(IRInst* inst, DiagnosticSink* sink) return false; } +static void unexportNonEmbeddableDXIL(IRModule* irModule) +{ + for (auto inst : irModule->getGlobalInsts()) + { + if (inst->getOp() == kIROp_Func) + { + // DXIL does not permit HLSLStructureBufferType in exported functions + // or sadly Matrices (https://github.com/shader-slang/slang/issues/4880) + auto type = as(inst->getFullType()); + auto argCount = type->getOperandCount(); + for (UInt aa = 0; aa < argCount; ++aa) + { + auto operand = type->getOperand(aa); + if (operand->getOp() == kIROp_HLSLStructuredBufferType || + operand->getOp() == kIROp_MatrixType) + { + if (auto dec = inst->findDecoration()) + { + dec->removeAndDeallocate(); + } + if (auto dec = inst->findDecoration()) + { + dec->removeAndDeallocate(); + } + break; + } + } + } + } +} + Result linkAndOptimizeIR( CodeGenContext* codeGenContext, LinkingAndOptimizationOptions const& options, @@ -746,6 +778,11 @@ Result linkAndOptimizeIR( break; } + if (codeGenContext->removeAvailableInDXIL) + { + removeAvailableInDownstreamModuleDecorations(irModule); + } + if (targetProgram->getOptionSet().shouldRunNonEssentialValidation()) { checkForRecursiveTypes(irModule, sink); @@ -1453,6 +1490,13 @@ Result linkAndOptimizeIR( auto metadata = new ArtifactPostEmitMetadata; outLinkedIR.metadata = metadata; + if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::EmbedDXIL)) + { + // We need to make sure that we don't try to export any functions that can't + // be part of a DXIL library interface, eg. with resources. + unexportNonEmbeddableDXIL(irModule); + } + collectMetadata(irModule, *metadata); outLinkedIR.metadata = metadata; diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index eb4e88c41f..09cb7952c8 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -790,6 +790,7 @@ INST_RANGE(BindingQuery, GetRegisterIndex, GetRegisterSpace) INST(PreciseDecoration, precise, 0, 0) INST(PublicDecoration, public, 0, 0) INST(HLSLExportDecoration, hlslExport, 0, 0) + INST(DownstreamModuleExportDecoration, downstreamModuleExport, 0, 0) INST(PatchConstantFuncDecoration, patchConstantFunc, 1, 0) INST(OutputControlPointsDecoration, outputControlPoints, 1, 0) INST(OutputTopologyDecoration, outputTopology, 1, 0) @@ -800,6 +801,8 @@ INST_RANGE(BindingQuery, GetRegisterIndex, GetRegisterSpace) INST(NumThreadsDecoration, numThreads, 3, 0) INST(WaveSizeDecoration, waveSize, 1, 0) + INST(AvailableInDXILDecoration, availableInDXIL, 0, 0) + // Added to IRParam parameters to an entry point /* GeometryInputPrimitiveTypeDecoration */ INST(PointInputPrimitiveTypeDecoration, pointPrimitiveType, 0, 0) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index f8836219e8..9b94005d99 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -430,6 +430,8 @@ IR_SIMPLE_DECORATION(NonCopyableTypeDecoration) IR_SIMPLE_DECORATION(HLSLMeshPayloadDecoration) IR_SIMPLE_DECORATION(GlobalInputDecoration) IR_SIMPLE_DECORATION(GlobalOutputDecoration) +IR_SIMPLE_DECORATION(AvailableInDXILDecoration) +IR_SIMPLE_DECORATION(DownstreamModuleExportDecoration) struct IRGLSLLocationDecoration : IRDecoration { diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index 8b08b9045c..94ba75a446 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -1461,7 +1461,7 @@ static bool _isHLSLExported(IRInst* inst) for (auto decoration : inst->getDecorations()) { const auto op = decoration->getOp(); - if (op == kIROp_HLSLExportDecoration) + if (op == kIROp_HLSLExportDecoration || op == kIROp_DownstreamModuleExportDecoration) { return true; } diff --git a/source/slang/slang-ir-metadata.cpp b/source/slang/slang-ir-metadata.cpp index 641c272a20..030fde1571 100644 --- a/source/slang/slang-ir-metadata.cpp +++ b/source/slang/slang-ir-metadata.cpp @@ -42,8 +42,18 @@ static void _insertBinding(List& ranges, LayoutResourceKind void collectMetadata(const IRModule* irModule, ArtifactPostEmitMetadata& outMetadata) { // Scan the instructions looking for global resource declarations + // and exported functions. for (const auto& inst : irModule->getGlobalInsts()) { + if (auto func = as(inst)) + { + if (func->findDecoration()) + { + auto name = func->findDecoration()->getMangledName(); + outMetadata.m_exportedFunctionMangledNames.add(name); + } + } + auto param = as(inst); if (!param) continue; diff --git a/source/slang/slang-ir-redundancy-removal.cpp b/source/slang/slang-ir-redundancy-removal.cpp index fa3cd444a8..038412fbfa 100644 --- a/source/slang/slang-ir-redundancy-removal.cpp +++ b/source/slang/slang-ir-redundancy-removal.cpp @@ -159,6 +159,36 @@ bool removeRedundancyInFunc(IRGlobalValueWithCode* func) return result; } +// Remove IR definitions from all [AvailableInDXIL] functions when compiling DXIL, +// as these functions are already defined in the embedded precompiled DXIL library. +void removeAvailableInDownstreamModuleDecorations(IRModule* module) +{ + List toRemove; + for (auto globalInst : module->getGlobalInsts()) + { + auto funcInst = as(globalInst); + if (!funcInst) + { + continue; + } + if (globalInst->findDecoration()) + { + // Gut the function definition, turning it into a declaration + for (auto inst : funcInst->getChildren()) + { + if (inst->getOp() == kIROp_Block) + { + toRemove.add(inst); + } + } + } + } + for (auto inst : toRemove) + { + inst->removeAndDeallocate(); + } +} + static IRInst* _getRootVar(IRInst* inst) { while (inst) diff --git a/source/slang/slang-ir-redundancy-removal.h b/source/slang/slang-ir-redundancy-removal.h index c2df7853e8..117f52708a 100644 --- a/source/slang/slang-ir-redundancy-removal.h +++ b/source/slang/slang-ir-redundancy-removal.h @@ -10,4 +10,6 @@ namespace Slang bool removeRedundancyInFunc(IRGlobalValueWithCode* func); bool eliminateRedundantLoadStore(IRGlobalValueWithCode* func); + + void removeAvailableInDownstreamModuleDecorations(IRModule* module); } diff --git a/source/slang/slang-ir-util.cpp b/source/slang/slang-ir-util.cpp index 817c10ec21..c4fc60bd22 100644 --- a/source/slang/slang-ir-util.cpp +++ b/source/slang/slang-ir-util.cpp @@ -244,6 +244,14 @@ bool isSimpleDataType(IRType* type) } } +bool isSimpleHLSLDataType(IRInst* inst) +{ + // TODO: Add criteria + // https://github.com/shader-slang/slang/issues/4792 + SLANG_UNUSED(inst); + return true; +} + SourceLoc findFirstUseLoc(IRInst* inst) { for (auto use = inst->firstUse; use; use = use->nextUse) diff --git a/source/slang/slang-ir-util.h b/source/slang/slang-ir-util.h index c7d6a15441..78d24d4411 100644 --- a/source/slang/slang-ir-util.h +++ b/source/slang/slang-ir-util.h @@ -99,6 +99,8 @@ bool isValueType(IRInst* type); bool isSimpleDataType(IRType* type); +bool isSimpleHLSLDataType(IRInst* inst); + SourceLoc findFirstUseLoc(IRInst* inst); inline bool isChildInstOf(IRInst* inst, IRInst* parent) diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index 99c62b214f..375107d1d4 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -2389,9 +2389,6 @@ struct IRModule : RefObject { return m_containerPool; } - - // TODO: make a map with lookup by target? - ComPtr precompiledDXIL; private: IRModule() = delete; diff --git a/source/slang/slang-module-library.cpp b/source/slang/slang-module-library.cpp index 42b9ff77a8..02ace07d3c 100644 --- a/source/slang/slang-module-library.cpp +++ b/source/slang/slang-module-library.cpp @@ -83,15 +83,6 @@ 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(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 12b32998f4..cf0dd2f20a 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -702,8 +702,6 @@ struct OptionsParser 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); SlangResult parse( @@ -1640,23 +1638,6 @@ 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) @@ -1948,7 +1929,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::EmbedDXIL: m_compileRequest->setEmbedDXIL(true); break; case OptionKind::Target: { CommandLineArg name; @@ -2795,11 +2776,6 @@ SlangResult OptionsParser::_parse( { 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.cpp b/source/slang/slang.cpp index 23e25249d7..4a6d33363b 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -3300,19 +3300,16 @@ SlangResult EndToEndCompileRequest::executeActionsInner() // 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 (getOptionSet().getBoolOption(CompilerOptionName::EmbedDXIL)) { - if (targetReq->getOptionSet().getBoolOption(CompilerOptionName::EmbedDXIL)) - { - auto frontEndReq = getFrontEndReq(); + auto frontEndReq = getFrontEndReq(); - for (auto translationUnit : frontEndReq->translationUnits) - { - SlangCompileTarget target = SlangCompileTarget(targetReq->getTarget()); - translationUnit->getModule()->precompileForTarget( - target, - nullptr); - } + for (auto translationUnit : frontEndReq->translationUnits) + { + SlangCompileTarget target = SlangCompileTarget(SlangCompileTarget::SLANG_DXIL); + SLANG_RETURN_ON_FAIL(translationUnit->getModule()->precompileForTarget( + target, + nullptr)); } } @@ -5935,9 +5932,9 @@ void EndToEndCompileRequest::setTargetGenerateWholeProgram(int targetIndex, bool getTargetOptionSet(targetIndex).set(CompilerOptionName::GenerateWholeProgram, value); } -void EndToEndCompileRequest::setTargetEmbedDXIL(int targetIndex, bool value) +void EndToEndCompileRequest::setEmbedDXIL(bool value) { - getTargetOptionSet(targetIndex).set(CompilerOptionName::EmbedDXIL, value); + getOptionSet().set(CompilerOptionName::EmbedDXIL, value); } void EndToEndCompileRequest::setTargetLineDirectiveMode( @@ -6861,4 +6858,4 @@ SlangResult EndToEndCompileRequest::isParameterLocationUsed(Int entryPointIndex, return SLANG_OK; } -} // namespace Slang \ No newline at end of file +} // namespace Slang diff --git a/tests/library/export-library-generics.slang b/tests/library/export-library-generics.slang new file mode 100644 index 0000000000..f88541da33 --- /dev/null +++ b/tests/library/export-library-generics.slang @@ -0,0 +1,39 @@ +//TEST_IGNORE_FILE: + +// export-library-generics.slang + +module "export-library-generics"; + +public cbuffer Constants { + public float x; + public float y; +} + +interface MyInterface +{ + int myMethod(int a); +} + +struct MyType : MyInterface +{ + int myMethod(int a) + { + return a * 3; + } +} + +int genericFunc(T arg) +{ + return arg.myMethod(3); +} + +public int normalFuncUsesGeneric(int a) +{ + MyType obj; + return genericFunc(obj); +} + +public int normalFunc(int a) +{ + return a - 2; +} diff --git a/tests/library/module-library-matrix.slang b/tests/library/module-library-matrix.slang new file mode 100644 index 0000000000..85e4685cc9 --- /dev/null +++ b/tests/library/module-library-matrix.slang @@ -0,0 +1,8 @@ +//TEST_IGNORE_FILE: + +module "module-library-matrix"; + +public float4x4 to4x4(float3x4 source) +{ + return float4x4(source[0], source[1], source[2], float4(0.0f, 0.0f, 0.0f, 1.0f)); +} diff --git a/tests/library/precompiled-dxil-generics.slang b/tests/library/precompiled-dxil-generics.slang new file mode 100644 index 0000000000..f01291a8ee --- /dev/null +++ b/tests/library/precompiled-dxil-generics.slang @@ -0,0 +1,28 @@ +// precompiled-dxil-generics.slang + +// A test that uses slang-modules with embedded precompiled DXIL and a library containing generics. +// The test compiles a library slang (export-library-generics.slang) with -embed-dxil then links the +// library to entrypoint slang (this file). +// The test passes if there is no errror thrown. +// TODO: Check if final linkage used only the precompiled dxil. + +//TEST(windows):COMPILE: tests/library/export-library-generics.slang -o tests/library/export-library-generics.slang-module -embed-dxil -profile lib_6_6 -incomplete-library +//TEST(windows):COMPILE: tests/library/precompiled-dxil-generics.slang -target dxil -stage anyhit -entry anyhit -o tests/library/linked.dxil + +import "export-library-generics"; + +struct Payload +{ + int val; +} + +struct Attributes +{ + float2 bary; +} + +[shader("anyhit")] +void anyhit(inout Payload payload, Attributes attrib) +{ + payload.val = normalFunc(x * y) + normalFuncUsesGeneric(y); +} diff --git a/tests/library/precompiled-dxil-matrix.slang b/tests/library/precompiled-dxil-matrix.slang new file mode 100644 index 0000000000..271a7e214b --- /dev/null +++ b/tests/library/precompiled-dxil-matrix.slang @@ -0,0 +1,26 @@ +// precompiled-dxil-matrix.slang + +// This test imports a precompiled module that exports a matrix type, which is known to +// cause https://github.com/shader-slang/slang/issues/4880 without driver mitigation. + +//TEST(windows):COMPILE: tests/library/module-library-matrix.slang -o tests/library/module-library-matrix.slang-module -embed-dxil -profile lib_6_6 -incomplete-library +//TEST(windows):COMPILE: tests/library/precompiled-dxil-matrix.slang -stage anyhit -entry shadow -target dxil -o precompiled-dxil-matrix.dxil + +import "module-library-matrix"; + +struct ShadowHitInfo +{ + bool isHit; + uint seed; +}; + +struct Attributes +{ + float2 bary; +}; + +[shader("anyhit")] +void shadow(inout ShadowHitInfo payload, Attributes attrib) +{ + IgnoreHit(); +} diff --git a/tests/library/precompiled-dxil.slang b/tests/library/precompiled-dxil.slang index 19f67b075c..8cc25bab5a 100644 --- a/tests/library/precompiled-dxil.slang +++ b/tests/library/precompiled-dxil.slang @@ -8,8 +8,7 @@ // TODO: Check if final linkage used only the precompiled dxil. //TEST(windows):COMPILE: tests/library/export-library.slang -o tests/library/export-library.slang-module -embed-dxil -profile lib_6_6 -incomplete-library -//TEST(windows):COMPILE: tests/library/precompiled-dxil.slang -o tests/library/precompiled-dxil.slang-module -embed-dxil -profile lib_6_6 -incomplete-library -//TEST(windows):COMPILE: tests/library/export-library.slang-module tests/library/precompiled-dxil.slang-module -target dxil -entry computeMain -profile cs_6_6 -o tests/library/linked.dxil +//TEST(windows):COMPILE: tests/library/precompiled-dxil.slang tests/library/export-library.slang-module -target dxil -entry computeMain -profile cs_6_6 -o tests/library/linked.dxil extern int foo(int a); diff --git a/tests/library/precompiled-module-library-resource.slang b/tests/library/precompiled-module-library-resource.slang new file mode 100644 index 0000000000..79c3daaa10 --- /dev/null +++ b/tests/library/precompiled-module-library-resource.slang @@ -0,0 +1,33 @@ +// precompiled-module-library-resource.slang + +// Compile this library source with -embed-dxil option. Tests that modules can be +// precompiled to dxil despite having resource parameters or return types. + +//TEST(windows):COMPILE: tests/library/precompiled-module-library-resource.slang -o tests/library/precompiled-module-library-resource.slang-module -embed-dxil -profile lib_6_6 -incomplete-library + +module "precompiled-module-library-resource"; + +public struct ResourceStruct { + public StructuredBuffer buffer; +}; + +public int resource_in_parameter(StructuredBuffer buffer) +{ + return buffer[0]; +} + +public int resource_in_struct_parameter(ResourceStruct rs) +{ + return rs.buffer[0]; +} + +internal int matrix_in_parameter_internal(int1x1 matrix) +{ + return matrix[0][0]; +} + +public int matrix_in_parameter_public(int a) +{ + int1x1 matrix = {a}; + return matrix_in_parameter_internal(matrix); +}