From 9abcb6ea24dbc7184c3a2ad9f4458f63f8901928 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Tue, 18 Aug 2020 13:42:46 -0400 Subject: Support for float atomics on RWByteAddressBuffer (#1502) * Fix premake5.lua so it uses the new path needed for OpenCLDebugInfo100.h * Keep including the includes directory. * Added the spirv-tools-generated files. * We don't need to include the spirv/unified1 path because the files needed are actually in the spirv-tools-generated folder. * Put the build_info.h glslang generated files in external/glslang-generated. Alter premake5.lua to pick up that header. * First pass at documenting how to build glslang and spirv-tools. * Improved glsl/spir-v tools README.md * Added revision.h * Change how gResources is calculated. Update about revision.h * Update docs a little. * Split out spirv-tools into a separate project for building glslang. This was not necessary on linux, but *is* necessary on windows, because there is a file disassemble.cpp in spirv-tools and in glslang, and this leads to VS choosing only one. With the separate library, the problem is resolved. * Fix direct-spirv-emit output. * Update to latest version of spirv headers and spirv-tools. * Upgrade submodule version of glslang in external. * Add fPIC to build options of slang-spirv-tools * WIP adding support for InterlockedAddFp32 * Upgrade slang-binaries to have new glslang. * Fix issues with Windows slang-glslang binaries, via update of slang-binaries used. * WIP - atomicAdd. This solution can't work as we can't do (float*) in glsl. * WIP on atomic float ops. * Added checking for multiple decls that takes into account __target_intrinsic and __specialized_for_target. First pass impl of atomic add on float for glsl. * Split __atomicAdd so extensions are applied appropriately. * Made Dxc/Fxc support includes. Use HLSL prelude to pass the path to nvapi Added -nv-api-path * Refactor around IncludeHandler and impl of IncludeSystem * slang-include-handler -> slang-include-system Have IncludeHandler/Impl defined in slang-preprocessor * Small comment improvements. * Document atomic float add addition in target-compatibility.md. * CUDA float atomic support on RWByteAddressBuffer. * Add atomic-float-byte-address-buffer-cross.slang * Removed inappropriate-once.slang - the test is no longer valid when a file is loaded and has a unique identity by default. A test could be made, but would require an API call to create the file (so no unique id). Improved handling of loadFile - uses uniqueId if has one. * Work around for testing target overlaps - to avoid exceptions on adding targets. Simplify PathInfo setup. Modify single-target-intrinsic.slang - it no longer failed because there were no longer multiple definitions for the same target. Co-authored-by: Tim Foley --- source/core/slang-test-tool-util.cpp | 45 ++++++-- source/core/slang-test-tool-util.h | 8 ++ source/slang/hlsl.meta.slang | 62 +++++++++++ source/slang/slang-check-decl.cpp | 91 ++++++++++++++-- source/slang/slang-compiler.cpp | 109 ++++++------------- source/slang/slang-compiler.h | 41 +------ source/slang/slang-dxc-support.cpp | 80 +++++++++++++- source/slang/slang-include-system.cpp | 139 ++++++++++++++++++++++++ source/slang/slang-include-system.h | 62 +++++++++++ source/slang/slang-ir-byte-address-legalize.cpp | 27 +++++ source/slang/slang-ir-inst-defs.h | 3 + source/slang/slang-preprocessor.h | 28 ++++- source/slang/slang-source-loc.cpp | 29 +++++ source/slang/slang-source-loc.h | 5 + source/slang/slang.cpp | 115 ++------------------ source/slang/slang.vcxproj | 2 + source/slang/slang.vcxproj.filters | 6 + 17 files changed, 604 insertions(+), 248 deletions(-) create mode 100644 source/slang/slang-include-system.cpp create mode 100644 source/slang/slang-include-system.h (limited to 'source') diff --git a/source/core/slang-test-tool-util.cpp b/source/core/slang-test-tool-util.cpp index f8e163758..5e31dc4d7 100644 --- a/source/core/slang-test-tool-util.cpp +++ b/source/core/slang-test-tool-util.cpp @@ -75,29 +75,54 @@ static SlangResult _addCUDAPrelude(const String& parentPath, slang::IGlobalSessi return SLANG_OK; } -/* static */SlangResult TestToolUtil::setSessionDefaultPrelude(const char* exePath, slang::IGlobalSession* session) +/* static */SlangResult TestToolUtil::setSessionDefaultPrelude(const PreludeInfo& info, slang::IGlobalSession* session) { // Set the prelude to a path - String canonicalPath; - if (SLANG_SUCCEEDED(Path::getCanonical(exePath, canonicalPath))) + if (info.exePath) { - // Get the directory - String parentPath = Path::getParentDirectory(canonicalPath); + String exePath(info.exePath); - if (SLANG_FAILED(_addCPPPrelude(parentPath, session))) + String canonicalPath; + if (SLANG_SUCCEEDED(Path::getCanonical(exePath, canonicalPath))) { - SLANG_ASSERT(!"Couldn't find the C++ prelude relative to the executable"); + // Get the directory + String parentPath = Path::getParentDirectory(canonicalPath); + + if (SLANG_FAILED(_addCPPPrelude(parentPath, session))) + { + SLANG_ASSERT(!"Couldn't find the C++ prelude relative to the executable"); + } + + if (SLANG_FAILED(_addCUDAPrelude(parentPath, session))) + { + SLANG_ASSERT(!"Couldn't find the CUDA prelude relative to the executable"); + } } - - if (SLANG_FAILED(_addCUDAPrelude(parentPath, session))) + } + // If the nvAPI path is set, and we find nvHLSLExtns.h, put that in the HLSL prelude + if (info.nvAPIPath) + { + String includePath; + if (SLANG_SUCCEEDED(_calcIncludePath(info.nvAPIPath, "nvHLSLExtns.h", includePath))) { - SLANG_ASSERT(!"Couldn't find the CUDA prelude relative to the executable"); + StringBuilder buf; + + buf << "#include \"" << includePath << "\"\n"; + + session->setLanguagePrelude(SLANG_SOURCE_LANGUAGE_HLSL, buf.getBuffer()); + return SLANG_OK; } } return SLANG_OK; } +/* static */SlangResult TestToolUtil::setSessionDefaultPrelude(const char* exePath, slang::IGlobalSession* session) +{ + PreludeInfo info; + info.exePath = exePath; + return setSessionDefaultPrelude(info, session); +} } diff --git a/source/core/slang-test-tool-util.h b/source/core/slang-test-tool-util.h index 9df2a6d6a..43bd49c9d 100644 --- a/source/core/slang-test-tool-util.h +++ b/source/core/slang-test-tool-util.h @@ -36,6 +36,12 @@ enum class ToolReturnCodeSpan /* Utility functions for 'test tools' */ struct TestToolUtil { + struct PreludeInfo + { + const char* exePath = nullptr; + const char* nvAPIPath = nullptr; + }; + typedef SlangResult(*InnerMainFunc)(Slang::StdWriters* stdWriters, SlangSession* session, int argc, const char*const* argv); /// If the test failed to run or was ignored then we are done @@ -48,6 +54,8 @@ struct TestToolUtil static ToolReturnCode getReturnCode(SlangResult res); /// Sets the default preludes on the session based on the executable path + static SlangResult setSessionDefaultPrelude(const PreludeInfo& preludeInfo, slang::IGlobalSession* session); + static SlangResult setSessionDefaultPrelude(const char* exePath, slang::IGlobalSession* session); }; diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 62a548555..329a73a33 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -48,6 +48,29 @@ struct ByteAddressBuffer } }; + +// Make the GLSL atomicAdd available. +// We have separate int/float implementations, as the float version requires some specific extensions +// https://www.khronos.org/registry/OpenGL/extensions/NV/NV_shader_atomic_float.txt + +__target_intrinsic(glsl, "atomicAdd($0, $1)") +__glsl_version(430) +__glsl_extension(GL_EXT_shader_atomic_float) +//__glsl_extension(GL_EXT_gpu_shader5) +float __atomicAdd(__ref float value, float amount); + +// Int versions require glsl 4.30 +// https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/atomicAdd.xhtml + +__target_intrinsic(glsl, "atomicAdd($0, $1)") +__glsl_version(430) +int __atomicAdd(__ref int value, int amount); + +__target_intrinsic(glsl, "atomicAdd($0, $1)") +__glsl_version(430) +uint __atomicAdd(__ref uint value, uint amount); + + __intrinsic_op($(kIROp_ByteAddressBufferLoad)) T __byteAddressBufferLoad(ByteAddressBuffer buffer, int offset); @@ -159,6 +182,41 @@ struct $(item.name) { return __byteAddressBufferLoad(this, location); } +${{{{ + if (item.op == kIROp_HLSLRWByteAddressBufferType) + { +}}}} + + // float32 and int64 atomic support. This is a Slang specific extension, it uses + // GL_EXT_shader_atomic_float on vk + // NvAPI support on DX + // NOTE! To use this feature on HLSL, the shader needs to include 'nvHLSLExtns.h' from the NvAPI SDK + // + __target_intrinsic(hlsl, "($3 = NvInterlockedAddFp32($0, $1, $2))") + __target_intrinsic(cuda, "(*$3 = atomicAdd((float*)$0._getPtrAt($1), $2))") + void InterlockedAddFp32(uint byteAddress, float valueToAdd, out float originalValue); + + __specialized_for_target(glsl) + void InterlockedAddFp32(uint byteAddress, float valueToAdd, out float originalValue) + { + RWStructuredBuffer buf = __getEquivalentStructuredBuffer(this); + originalValue = __atomicAdd(buf[byteAddress / 4], valueToAdd); + } + + __target_intrinsic(hlsl, "(NvInterlockedAddFp32($0, $1, $2))") + __target_intrinsic(cuda, "atomicAdd((float*)$0._getPtrAt($1), $2)") + void InterlockedAddFp32(uint byteAddress, float valueToAdd); + + __specialized_for_target(glsl) + void InterlockedAddFp32(uint byteAddress, float valueToAdd) + { + RWStructuredBuffer buf = __getEquivalentStructuredBuffer(this); + __atomicAdd(buf[byteAddress / 4], valueToAdd); + } + +${{{{ + } +}}}} // Added operations: @@ -1091,6 +1149,10 @@ T dot(vector x, vector y) __generic vector dst(vector x, vector y); +// Given a RWByteAddressBuffer allow it to be interpretted as a RWStructuredBuffer +__intrinsic_op($(kIROp_GetEquivalentStructuredBuffer)) +RWStructuredBuffer __getEquivalentStructuredBuffer(RWByteAddressBuffer b); + // Error message // void errorf( string format, ... ); diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index f965f9759..13b818b26 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -3520,6 +3520,7 @@ namespace Slang return subst; } +#if 0 // For simplicity we will make having a definition of a function include having a body or a target intrinsics defined. // It may be useful to add other modifiers to mark as having body - for example perhaps // any target intrinsic modifier (like SPIR-V version) should be included. @@ -3536,6 +3537,40 @@ namespace Slang { return decl->body || decl->hasModifier(); } +#endif + + typedef Dictionary TargetDeclDictionary; + + static void _addTargetModifiers(CallableDecl* decl, TargetDeclDictionary& ioDict) + { + if (auto specializedModifier = decl->findModifier()) + { + // If it's specialized for target it should have a body... + if (auto funcDecl = as(decl)) + { + SLANG_ASSERT(funcDecl->body); + } + Name* targetName = specializedModifier->targetToken.getName(); + + ioDict.AddIfNotExists(targetName, decl); + } + else + { + for (auto modifier : decl->getModifiersOfType()) + { + Name* targetName = modifier->targetToken.getName(); + ioDict.AddIfNotExists(targetName, decl); + } + + auto funcDecl = as(decl); + if (funcDecl && funcDecl->body) + { + // Should only be one body if it isn't specialized for target. + // Use nullptr for this scenario + ioDict.AddIfNotExists(nullptr, decl); + } + } + } Result SemanticsVisitor::checkFuncRedeclaration( FuncDecl* newDecl, @@ -3701,23 +3736,55 @@ namespace Slang // with the case where the two function declarations // might represent different target-specific versions // of a function. - // - // TODO: if the two declarations are specialized for - // different targets, then skip the body checks below. - // - // ???: Why isn't this problem showing up in practice? - + // If both of the declarations have a body, then there // is trouble, because we wouldn't know which one to // use during code generation. - if (_isDefinition(newDecl) && _isDefinition(oldDecl)) + + // Here to cover the 'bodies'/target_intrinsics, we find all the targets that + // that are previously defined, and make sure the new definition + // doesn't try and define what is already defined. { - // Redefinition - getSink()->diagnose(newDecl, Diagnostics::functionRedefinition, newDecl->getName()); - getSink()->diagnose(oldDecl, Diagnostics::seePreviousDefinitionOf, newDecl->getName()); + TargetDeclDictionary currentTargets; + { + CallableDecl* curDecl = newDecl->primaryDecl; + while (curDecl) + { + if (curDecl != newDecl) + { + _addTargetModifiers(curDecl, currentTargets); + } + curDecl = curDecl->nextDecl; + } + } - // Don't bother emitting other errors - return SLANG_FAIL; + // Add the targets for this new decl + TargetDeclDictionary newTargets; + _addTargetModifiers(newDecl, newTargets); + + bool hasConflict = false; + for (auto& pair : newTargets) + { + Name* target = pair.Key; + auto found = currentTargets.TryGetValue(target); + if (found) + { + // Redefinition + if (!hasConflict) + { + getSink()->diagnose(newDecl, Diagnostics::functionRedefinition, newDecl->getName()); + hasConflict = true; + } + + auto prevDecl = *found; + getSink()->diagnose(prevDecl, Diagnostics::seePreviousDefinitionOf, prevDecl->getName()); + } + } + + if (hasConflict) + { + return SLANG_FAIL; + } } // At this point we've processed the redeclaration and diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index c643f825a..72cf2dfbe 100755 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -8,7 +8,6 @@ #include "../core/slang-riff.h" #include "../core/slang-type-text-util.h" - #include "slang-check.h" #include "slang-compiler.h" #include "slang-lexer.h" @@ -841,83 +840,39 @@ namespace Slang return UnownedStringSlice(); } - /// Read a file in the context of handling a preprocessor directive - static SlangResult readFile( - Linkage* linkage, - String const& path, - ISlangBlob** outBlob) - { - // The actual file loading will be handled by the file system - // associated with the parent linkage. - // - auto fileSystemExt = linkage->getFileSystemExt(); - SLANG_RETURN_ON_FAIL(fileSystemExt->loadFile(path.getBuffer(), outBlob)); - - return SLANG_OK; - } - struct FxcIncludeHandler : ID3DInclude { - Linkage* linkage; - DiagnosticSink* sink; - IncludeHandler* includeHandler; - PathInfo rootPathInfo; - - STDMETHOD(Open)(D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes) override + + STDMETHOD(Open)(D3D_INCLUDE_TYPE includeType, LPCSTR fileName, LPCVOID parentData, LPCVOID* outData, UINT* outSize) override { - SLANG_UNUSED(IncludeType); - SLANG_UNUSED(pParentData); - - String path(pFileName); + SLANG_UNUSED(includeType); + // NOTE! The pParentData means the *text* of any previous include. + // In order to work out what *path* that came from, we need to seach which source file it came from, and + // use it's path - SourceLoc loc; + // Assume the root pathInfo initially + PathInfo includedFromPathInfo = m_rootPathInfo; - PathInfo includedFromPathInfo = rootPathInfo; - - if (!includeHandler) + // Lets try and find the parent source if there is any + if (parentData) { - return SLANG_E_NOT_IMPLEMENTED; - } - - // Find the path relative to the foundPath - PathInfo filePathInfo; - if (SLANG_FAILED(includeHandler->findFile(path, includedFromPathInfo.foundPath, filePathInfo))) - { - return SLANG_E_CANNOT_OPEN; - } - - // We must have a uniqueIdentity to be compare - if (!filePathInfo.hasUniqueIdentity()) - { - return SLANG_E_ABORT; - } - - // Simplify the path - filePathInfo.foundPath = includeHandler->simplifyPath(filePathInfo.foundPath); - - // See if this an already loaded source file - auto sourceManager = linkage->getSourceManager(); - SourceFile* sourceFile = sourceManager->findSourceFileRecursively(filePathInfo.uniqueIdentity); - - // If not create a new one, and add to the list of known source files - if (!sourceFile) - { - ComPtr foundSourceBlob; - if (SLANG_FAILED(readFile(linkage, filePathInfo.foundPath, foundSourceBlob.writeRef()))) + SourceFile* foundSourceFile = m_system.getSourceManager()->findSourceFileByContentRecursively((const char*)parentData); + if (foundSourceFile) { - return SLANG_E_CANNOT_OPEN; + includedFromPathInfo = foundSourceFile->getPathInfo(); } - - sourceFile = sourceManager->createSourceFileWithBlob(filePathInfo, foundSourceBlob); - sourceManager->addSourceFile(filePathInfo.uniqueIdentity, sourceFile); } - // This is a new parse (even if it's a pre-existing source file), so create a new SourceUnit - SourceView* sourceView = sourceManager->createSourceView(sourceFile, &filePathInfo); + String path(fileName); + PathInfo pathInfo; + ComPtr blob; - *ppData = sourceView->getContent().begin(); - *pBytes = (UINT) sourceView->getContentSize(); + SLANG_RETURN_ON_FAIL(m_system.findAndLoadFile(path, includedFromPathInfo.foundPath, pathInfo, blob)); + // Return the data + *outData = blob->getBufferPointer(); + *outSize = (UINT) blob->getBufferSize(); + return S_OK; } @@ -926,6 +881,13 @@ namespace Slang SLANG_UNUSED(pData); return S_OK; } + FxcIncludeHandler(SearchDirectoryList* searchDirectories, ISlangFileSystemExt* fileSystemExt, SourceManager* sourceManager): + m_system(searchDirectories, fileSystemExt, sourceManager) + { + } + + PathInfo m_rootPathInfo; + IncludeSystem m_system; }; SlangResult emitDXBytecodeForEntryPoint( @@ -967,13 +929,9 @@ namespace Slang List dxMacrosStorage; D3D_SHADER_MACRO const* dxMacros = nullptr; - IncludeHandlerImpl includeHandler; - includeHandler.linkage = linkage; - includeHandler.searchDirectories = &linkage->searchDirectories; - - FxcIncludeHandler fxcIncludeHandlerStorage; - FxcIncludeHandler* fxcIncludeHandler = nullptr; - + FxcIncludeHandler fxcIncludeHandlerStorage(&linkage->searchDirectories, linkage->getFileSystemExt(), sink->getSourceManager()); + FxcIncludeHandler* fxcIncludeHandler = &fxcIncludeHandlerStorage; + if(auto translationUnit = findPassThroughTranslationUnit(endToEndReq, entryPointIndex)) { for( auto& define : translationUnit->compileRequest->preprocessorDefinitions ) @@ -996,10 +954,7 @@ namespace Slang dxMacros = dxMacrosStorage.getBuffer(); fxcIncludeHandler = &fxcIncludeHandlerStorage; - fxcIncludeHandler->linkage = linkage; - fxcIncludeHandler->sink = compileRequest->getSink(); - fxcIncludeHandler->includeHandler = &includeHandler; - fxcIncludeHandler->rootPathInfo = translationUnit->m_sourceFiles[0]->getPathInfo(); + fxcIncludeHandlerStorage.m_rootPathInfo = translationUnit->m_sourceFiles[0]->getPathInfo(); } DWORD flags = 0; diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 19f56031d..8996f5512 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -16,6 +16,8 @@ #include "slang-file-system.h" +#include "slang-include-system.h" + #include "slang-ir-serialize-types.h" #include "../../slang.h" @@ -1169,28 +1171,6 @@ namespace Slang Profile getEffectiveProfile(EntryPoint* entryPoint, TargetRequest* target); - // A directory to be searched when looking for files (e.g., `#include`) - struct SearchDirectory - { - SearchDirectory() = default; - SearchDirectory(SearchDirectory const& other) = default; - SearchDirectory(String const& path) - : path(path) - {} - - String path; - }; - - /// A list of directories to search for files (e.g., `#include`) - struct SearchDirectoryList - { - // A parent list that should also be searched - SearchDirectoryList* parent = nullptr; - - // Directories to be searched - List searchDirectories; - }; - /// Given a target returns the required downstream compiler PassThroughMode getDownstreamCompilerRequiredForTarget(CodeGenTarget target); /// Given a target returns a downstream compiler the prelude should be taken from. @@ -2163,23 +2143,6 @@ namespace Slang PassThroughMode m_defaultDownstreamCompilers[int(SourceLanguage::CountOf)]; }; -struct IncludeHandlerImpl : IncludeHandler -{ - Linkage* linkage; - SearchDirectoryList* searchDirectories; - - ISlangFileSystemExt* _getFileSystemExt(); - - SlangResult _findFile(SlangPathType fromPathType, const String& fromPath, const String& path, PathInfo& pathInfoOut); - - virtual SlangResult findFile( - String const& pathToInclude, - String const& pathIncludedFrom, - PathInfo& pathInfoOut) override; - - virtual String simplifyPath(const String& path) override; -}; - // // The following functions are utilties to convert between diff --git a/source/slang/slang-dxc-support.cpp b/source/slang/slang-dxc-support.cpp index 6f8151921..b0938ed12 100755 --- a/source/slang/slang-dxc-support.cpp +++ b/source/slang/slang-dxc-support.cpp @@ -45,6 +45,82 @@ namespace Slang return UnownedStringSlice(); } + // IDxcIncludeHandler + // 7f61fc7d-950d-467f-b3e3-3c02fb49187c + static const Guid IID_IDxcIncludeHandler = { 0x7f61fc7d, 0x950d, 0x467f, { 0x3c, 0x02, 0xfb, 0x49, 0x18, 0x7c } }; + static const Guid IID_IUnknown = SLANG_UUID_ISlangUnknown; + + class DxcIncludeHandler : public IDxcIncludeHandler, public RefObject + { + public: + // Implement IUnknown + SLANG_NO_THROW HRESULT SLANG_MCALL QueryInterface(const IID& uuid, void** out) + { + ISlangUnknown* intf = getInterface(reinterpret_cast(uuid)); + if (intf) + { + addReference(); + *out = intf; + return SLANG_OK; + } + return SLANG_E_NO_INTERFACE; + } + SLANG_NO_THROW ULONG SLANG_MCALL AddRef() SLANG_OVERRIDE { return (uint32_t)addReference(); } + SLANG_NO_THROW ULONG SLANG_MCALL Release() SLANG_OVERRIDE { return (uint32_t)releaseReference(); } + + // Implement IDxcIncludeHandler + virtual HRESULT SLANG_MCALL LoadSource(LPCWSTR inFilename, IDxcBlob** outSource) SLANG_OVERRIDE + { + // Hmm DXC does something a bit odd - when it sees a path, it just passes that in with ./ in front!! + // NOTE! It doesn't make any difference if it is "" or <> quoted. + + // So we just do a work around where we strip if we see a path starting with ./ + String filePath = String::fromWString(inFilename); + + // If it starts with ./ then attempt to strip it + if (filePath.startsWith("./")) + { + String remaining(filePath.subString(2, filePath.getLength() - 2)); + + // Okay if we strip ./ and what we have is absolute, then it's the absolute path that we care about, + // otherwise we just leave as is. + if (Path::isAbsolute(remaining)) + { + filePath = remaining; + } + } + + ComPtr blob; + PathInfo pathInfo; + SlangResult res = m_system.findAndLoadFile(filePath, String(), pathInfo, blob); + + // NOTE! This only works because ISlangBlob is *binary compatible* with IDxcBlob, if either + // change things could go boom + *outSource = (IDxcBlob*)blob.detach(); + return res; + } + + DxcIncludeHandler(SearchDirectoryList* searchDirectories, ISlangFileSystemExt* fileSystemExt, SourceManager* sourceManager = nullptr) : + m_system(searchDirectories, fileSystemExt, sourceManager) + { + } + + protected: + + // Used by QueryInterface for casting + ISlangUnknown* getInterface(const Guid& guid) + { + if (guid == IID_IUnknown || guid == IID_IDxcIncludeHandler) + { + return (ISlangUnknown*)(static_cast(this)); + } + return nullptr; + } + + IncludeSystem m_system; + }; + + SlangResult emitDXILForEntryPointUsingDXC( ComponentType* program, BackEndCompileRequest* compileRequest, @@ -194,6 +270,8 @@ namespace Slang const String sourcePath = calcSourcePathForEntryPoint(endToEndReq, entryPointIndex); + ComPtr includeHandler(new DxcIncludeHandler(&linkage->searchDirectories, linkage->getFileSystemExt())); + ComPtr dxcResult; SLANG_RETURN_ON_FAIL(dxcCompiler->Compile(dxcSourceBlob, sourcePath.toWString().begin(), @@ -203,7 +281,7 @@ namespace Slang argCount, nullptr, // `#define`s 0, // `#define` count - nullptr, // `#include` handler + includeHandler, // `#include` handler dxcResult.writeRef())); // Retrieve result. diff --git a/source/slang/slang-include-system.cpp b/source/slang/slang-include-system.cpp new file mode 100644 index 000000000..66aeb3f67 --- /dev/null +++ b/source/slang/slang-include-system.cpp @@ -0,0 +1,139 @@ +// slang-include-system.cpp +#include "slang-include-system.h" + +#include "../core/slang-io.h" +#include "../core/slang-string-util.h" + +namespace Slang +{ + +SlangResult IncludeSystem::findFile(SlangPathType fromPathType, const String& fromPath, const String& path, PathInfo& outPathInfo) +{ + // Get relative path + ComPtr combinedPathBlob; + SLANG_RETURN_ON_FAIL(m_fileSystemExt->calcCombinedPath(fromPathType, fromPath.begin(), path.begin(), combinedPathBlob.writeRef())); + String combinedPath(StringUtil::getString(combinedPathBlob)); + if (combinedPath.getLength() <= 0) + { + return SLANG_FAIL; + } + + SlangPathType pathType; + SLANG_RETURN_ON_FAIL(m_fileSystemExt->getPathType(combinedPath.begin(), &pathType)); + if (pathType != SLANG_PATH_TYPE_FILE) + { + return SLANG_E_NOT_FOUND; + } + + // Get the uniqueIdentity + ComPtr uniqueIdentityBlob; + SLANG_RETURN_ON_FAIL(m_fileSystemExt->getFileUniqueIdentity(combinedPath.begin(), uniqueIdentityBlob.writeRef())); + + // If the rel path exists -> a uniqueIdentity MUST exists too + String uniqueIdentity(StringUtil::getString(uniqueIdentityBlob)); + if (uniqueIdentity.getLength() <= 0) + { + // Unique identity can't be empty + return SLANG_FAIL; + } + + outPathInfo = PathInfo::makeNormal(combinedPath, uniqueIdentity); + return SLANG_OK; +} + +String IncludeSystem::simplifyPath(const String& path) +{ + ComPtr simplifiedPath; + if (SLANG_FAILED(m_fileSystemExt->getSimplifiedPath(path.getBuffer(), simplifiedPath.writeRef()))) + { + return path; + } + return StringUtil::getString(simplifiedPath); +} + +SlangResult IncludeSystem::findFile(String const& pathToInclude, String const& pathIncludedFrom, PathInfo& outPathInfo) +{ + outPathInfo.type = PathInfo::Type::Unknown; + + // Try just relative to current path + { + SlangResult res = findFile(SLANG_PATH_TYPE_FILE, pathIncludedFrom, pathToInclude, outPathInfo); + // It either succeeded or wasn't found, anything else is a failure passed back + if (SLANG_SUCCEEDED(res) || res != SLANG_E_NOT_FOUND) + { + return res; + } + } + + // Search all the searchDirectories + for (auto sd = m_searchDirectories; sd; sd = sd->parent) + { + for (auto& dir : sd->searchDirectories) + { + SlangResult res = findFile(SLANG_PATH_TYPE_DIRECTORY, dir.path, pathToInclude, outPathInfo); + if (SLANG_SUCCEEDED(res) || res != SLANG_E_NOT_FOUND) + { + return res; + } + } + } + + return SLANG_E_NOT_FOUND; +} + +SlangResult IncludeSystem::loadFile(const PathInfo& pathInfo, ComPtr& outBlob) +{ + if (m_sourceManager) + { + // See if this an already loaded source file + SourceFile* sourceFile = m_sourceManager->findSourceFileRecursively(pathInfo.uniqueIdentity); + + // If not create a new one, and add to the list of known source files + if (!sourceFile) + { + ComPtr foundSourceBlob; + if (SLANG_FAILED(m_fileSystemExt->loadFile(pathInfo.foundPath.getBuffer(), foundSourceBlob.writeRef()))) + { + return SLANG_E_CANNOT_OPEN; + } + + sourceFile = m_sourceManager->createSourceFileWithBlob(pathInfo, foundSourceBlob); + m_sourceManager->addSourceFile(pathInfo.uniqueIdentity, sourceFile); + + outBlob = foundSourceBlob; + return SLANG_OK; + } + else + { + if (sourceFile->getContentBlob()) + { + outBlob = sourceFile->getContentBlob(); + return SLANG_OK; + } + + ComPtr foundSourceBlob; + if (SLANG_FAILED(m_fileSystemExt->loadFile(pathInfo.foundPath.getBuffer(), foundSourceBlob.writeRef()))) + { + return SLANG_E_CANNOT_OPEN; + } + + sourceFile->setContents(foundSourceBlob); + outBlob = foundSourceBlob; + return SLANG_OK; + } + } + else + { + // If we don't have the source manager, just load + return m_fileSystemExt->loadFile(pathInfo.foundPath.getBuffer(), outBlob.writeRef()); + } +} + +SlangResult IncludeSystem::findAndLoadFile(const String& pathToInclude, const String& pathIncludedFrom, PathInfo& outPathInfo, ComPtr& outBlob) +{ + SLANG_RETURN_ON_FAIL(findFile(pathToInclude, pathIncludedFrom, outPathInfo)); + SLANG_RETURN_ON_FAIL(loadFile(outPathInfo, outBlob)); + return SLANG_OK; +} + +} // namespace Slang diff --git a/source/slang/slang-include-system.h b/source/slang/slang-include-system.h new file mode 100644 index 000000000..3b368b70d --- /dev/null +++ b/source/slang/slang-include-system.h @@ -0,0 +1,62 @@ +#ifndef SLANG_INCLUDE_SYSTEM_H +#define SLANG_INCLUDE_SYSTEM_H +// slang-include-system.h + +#include "slang-source-loc.h" + +namespace Slang +{ + +// A directory to be searched when looking for files (e.g., `#include`) +struct SearchDirectory +{ + SearchDirectory() = default; + SearchDirectory(SearchDirectory const& other) = default; + SearchDirectory(String const& path) + : path(path) + {} + + String path; +}; + +/// A list of directories to search for files (e.g., `#include`) +struct SearchDirectoryList +{ + // A parent list that should also be searched + SearchDirectoryList* parent = nullptr; + + // Directories to be searched + List searchDirectories; +}; + +/* A helper class that builds basic include handling on top of searchDirectories/fileSystemExt and optionally a sourceManager */ +struct IncludeSystem +{ + IncludeSystem(SearchDirectoryList* searchDirectories, ISlangFileSystemExt* fileSystemExt, SourceManager* sourceManager = nullptr) : + m_searchDirectories(searchDirectories), + m_fileSystemExt(fileSystemExt), + m_sourceManager(sourceManager) + { + } + + SlangResult findFile(const String& pathToInclude, const String& pathIncludedFrom, PathInfo& outPathInfo); + SlangResult findFile(SlangPathType fromPathType, const String& fromPath, const String& path, PathInfo& outPathInfo); + String simplifyPath(const String& path); + SlangResult loadFile(const PathInfo& pathInfo, ComPtr& outBlob); + + SlangResult findAndLoadFile(const String& pathToInclude, const String& pathIncludedFrom, PathInfo& outPathInfo, ComPtr& outBlob); + + SearchDirectoryList* getSearchDirectoryList() const { return m_searchDirectories; } + ISlangFileSystemExt* getFileSystem() const { return m_fileSystemExt; } + SourceManager* getSourceManager() const { return m_sourceManager; } + +protected: + + SearchDirectoryList* m_searchDirectories; + ISlangFileSystemExt* m_fileSystemExt; + SourceManager* m_sourceManager; ///< If not set, will not look up the content in the source manager +}; + +} + +#endif // SLANG_INCLUDE_HANDLER_H diff --git a/source/slang/slang-ir-byte-address-legalize.cpp b/source/slang/slang-ir-byte-address-legalize.cpp index e33408855..ed54a3ef6 100644 --- a/source/slang/slang-ir-byte-address-legalize.cpp +++ b/source/slang/slang-ir-byte-address-legalize.cpp @@ -61,8 +61,13 @@ struct ByteAddressBufferLegalizationContext case kIROp_ByteAddressBufferStore: processStore(inst); break; + + case kIROp_GetEquivalentStructuredBuffer: + processGetEquivalentStructuredBuffer(inst); + break; } + IRInst* nextChild = nullptr; for( IRInst* child = inst->getFirstChild(); child; child = nextChild ) { @@ -71,6 +76,28 @@ struct ByteAddressBufferLegalizationContext } } + void processGetEquivalentStructuredBuffer(IRInst* inst) + { + // We need to see what type it is to be interpreted as. + auto type = inst->getDataType(); + + // We want to determine the element type + auto structuredBufferType = as(type); + auto elementType = structuredBufferType->getElementType(); + + // The buffer is operand 0 + auto buffer = inst->getOperand(0); + + // Get the equivalent structured buffer for the buffer. + if( auto structuredBuffer = getEquivalentStructuredBuffer(elementType, buffer) ) + { + // We want to replace the the inst, with the equivalent structured buffer reference + inst->replaceUsesWith(structuredBuffer); + // Once replaced we don't need anymore + inst->removeAndDeallocate(); + } + } + // The logic for both the load and store cases is similar, // so we will present the entire load case first and then // move on to stores. diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 74bd15531..f60f84a9e 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -590,6 +590,9 @@ INST(ExtractTaggedUnionPayload, extractTaggedUnionPayload, 1, 0) INST(BitCast, bitCast, 1, 0) +// Converts other resources (such as ByteAddressBuffer) to the equivalent StructuredBuffer +INST(GetEquivalentStructuredBuffer, getEquivalentStructuredBuffer, 1, 0) + /* Layout */ INST(VarLayout, varLayout, 1, 0) diff --git a/source/slang/slang-preprocessor.h b/source/slang/slang-preprocessor.h index 191adce88..0e7509a27 100644 --- a/source/slang/slang-preprocessor.h +++ b/source/slang/slang-preprocessor.h @@ -3,7 +3,10 @@ #define SLANG_PREPROCESSOR_H_INCLUDED #include "../core/slang-basic.h" -#include "../slang/slang-lexer.h" + +#include "slang-lexer.h" + +#include "slang-include-system.h" namespace Slang { @@ -16,14 +19,27 @@ class ModuleDecl; // for files in `#include` directives. struct IncludeHandler { - - virtual SlangResult findFile(const String& pathToInclude, - const String& pathIncludedFrom, - PathInfo& pathInfoOut) = 0; - + virtual SlangResult findFile(const String& pathToInclude, const String& pathIncludedFrom, PathInfo& outPathInfo) = 0; virtual String simplifyPath(const String& path) = 0; }; +// A default implementation that uses IncludeSystem to implement functionality +struct IncludeHandlerImpl : IncludeHandler +{ + virtual SlangResult findFile(const String& pathToInclude, const String& pathIncludedFrom, PathInfo& outPathInfo) override + { + return m_system.findFile(pathToInclude, pathIncludedFrom, outPathInfo); + } + virtual String simplifyPath(const String& path) override { return m_system.simplifyPath(path); } + + IncludeHandlerImpl(SearchDirectoryList* searchDirectories, ISlangFileSystemExt* fileSystemExt, SourceManager* sourceManager = nullptr) : + m_system(searchDirectories, fileSystemExt, sourceManager) + { + } +protected: + IncludeSystem m_system; +}; + // Take a string of source code and preprocess it into a list of tokens. TokenList preprocessSource( SourceFile* file, diff --git a/source/slang/slang-source-loc.cpp b/source/slang/slang-source-loc.cpp index eb2ea6b1b..7eca23d8d 100644 --- a/source/slang/slang-source-loc.cpp +++ b/source/slang/slang-source-loc.cpp @@ -521,6 +521,35 @@ SourceFile* SourceManager::findSourceFileRecursively(const String& uniqueIdentit return nullptr; } +SourceFile* SourceManager::findSourceFileByContentRecursively(const char* text) +{ + const SourceManager* manager = this; + do + { + SourceFile* sourceFile = manager->findSourceFileByContent(text); + if (sourceFile) + { + return sourceFile; + } + manager = manager->m_parent; + } while (manager); + return nullptr; +} + +SourceFile* SourceManager::findSourceFileByContent(const char* text) const +{ + for (SourceFile* sourceFile : getSourceFiles()) + { + auto content = sourceFile->getContent(); + + if (text >= content.begin() && text <= content.end()) + { + return sourceFile; + } + } + return nullptr; +} + void SourceManager::addSourceFile(const String& uniqueIdentity, SourceFile* sourceFile) { SLANG_ASSERT(!findSourceFileRecursively(uniqueIdentity)); diff --git a/source/slang/slang-source-loc.h b/source/slang/slang-source-loc.h index 51d826108..c00120476 100644 --- a/source/slang/slang-source-loc.h +++ b/source/slang/slang-source-loc.h @@ -350,6 +350,11 @@ struct SourceManager /// Find if the source file is defined on this manager. SourceFile* findSourceFile(const String& uniqueIdentity) const; + /// Searches this manager, and then the parent to see if can find a match + SourceFile* findSourceFileByContentRecursively(const char* text); + /// Find the source file that contains *the memory* text points to. + SourceFile* findSourceFileByContent(const char* text) const; + /// Get the file system associated with this source manager ISlangFileSystemExt* getFileSystemExt() const { return m_fileSystemExt; } /// Get the file system associated with this source manager diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 8523a445d..aed38547d 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -329,96 +329,6 @@ DownstreamCompiler* Session::getDefaultDownstreamCompiler(SourceLanguage sourceL return getOrLoadDownstreamCompiler(m_defaultDownstreamCompilers[int(sourceLanguage)], nullptr); } -ISlangFileSystemExt* IncludeHandlerImpl::_getFileSystemExt() -{ - return linkage->getFileSystemExt(); -} - -SlangResult IncludeHandlerImpl::_findFile(SlangPathType fromPathType, const String& fromPath, const String& path, PathInfo& pathInfoOut) -{ - ISlangFileSystemExt* fileSystemExt = _getFileSystemExt(); - - // Get relative path - ComPtr combinedPathBlob; - SLANG_RETURN_ON_FAIL(fileSystemExt->calcCombinedPath(fromPathType, fromPath.begin(), path.begin(), combinedPathBlob.writeRef())); - String combinedPath(StringUtil::getString(combinedPathBlob)); - if (combinedPath.getLength() <= 0) - { - return SLANG_FAIL; - } - - SlangPathType pathType; - SLANG_RETURN_ON_FAIL(fileSystemExt->getPathType(combinedPath.begin(), &pathType)); - if (pathType != SLANG_PATH_TYPE_FILE) - { - return SLANG_E_NOT_FOUND; - } - - // Get the uniqueIdentity - ComPtr uniqueIdentityBlob; - SLANG_RETURN_ON_FAIL(fileSystemExt->getFileUniqueIdentity(combinedPath.begin(), uniqueIdentityBlob.writeRef())); - - // If the rel path exists -> a uniqueIdentity MUST exists too - String uniqueIdentity(StringUtil::getString(uniqueIdentityBlob)); - if (uniqueIdentity.getLength() <= 0) - { - // Unique identity can't be empty - return SLANG_FAIL; - } - - pathInfoOut.type = PathInfo::Type::Normal; - pathInfoOut.foundPath = combinedPath; - pathInfoOut.uniqueIdentity = uniqueIdentity; - return SLANG_OK; -} - -SlangResult IncludeHandlerImpl::findFile( - String const& pathToInclude, - String const& pathIncludedFrom, - PathInfo& pathInfoOut) -{ - pathInfoOut.type = PathInfo::Type::Unknown; - - // Try just relative to current path - { - SlangResult res = _findFile(SLANG_PATH_TYPE_FILE, pathIncludedFrom, pathToInclude, pathInfoOut); - // It either succeeded or wasn't found, anything else is a failure passed back - if (SLANG_SUCCEEDED(res) || res != SLANG_E_NOT_FOUND) - { - return res; - } - } - - // Search all the searchDirectories - for(auto sd = searchDirectories; sd; sd = sd->parent) - { - for(auto& dir : sd->searchDirectories) - { - SlangResult res = _findFile(SLANG_PATH_TYPE_DIRECTORY, dir.path, pathToInclude, pathInfoOut); - if (SLANG_SUCCEEDED(res) || res != SLANG_E_NOT_FOUND) - { - return res; - } - } - } - - return SLANG_E_NOT_FOUND; -} - -String IncludeHandlerImpl::simplifyPath(const String& path) -{ - ISlangFileSystemExt* fileSystemExt = _getFileSystemExt(); - ComPtr simplifiedPath; - if (SLANG_FAILED(fileSystemExt->getSimplifiedPath(path.getBuffer(), simplifiedPath.writeRef()))) - { - return path; - } - return StringUtil::getString(simplifiedPath); -} - -// - - Profile getEffectiveProfile(EntryPoint* entryPoint, TargetRequest* target) { auto entryPointProfile = entryPoint->getProfile(); @@ -866,12 +776,16 @@ SlangResult Linkage::loadFile(String const& path, PathInfo& outPathInfo, ISlangB ComPtr uniqueIdentity; // Get the unique identity - SLANG_RETURN_ON_FAIL(m_fileSystemExt->getFileUniqueIdentity(path.getBuffer(), uniqueIdentity.writeRef())); - - outPathInfo.foundPath = path; - outPathInfo.type = PathInfo::Type::FoundPath; - outPathInfo.uniqueIdentity = StringUtil::getString(uniqueIdentity); - + if (SLANG_FAILED(m_fileSystemExt->getFileUniqueIdentity(path.getBuffer(), uniqueIdentity.writeRef()))) + { + // We didn't get a unique identity, so go with just a found path + outPathInfo.type = PathInfo::Type::FoundPath; + outPathInfo.foundPath = path; + } + else + { + outPathInfo = PathInfo::makeNormal(path, StringUtil::getString(uniqueIdentity)); + } return SLANG_OK; } @@ -975,8 +889,6 @@ FrontEndCompileRequest::FrontEndCompileRequest( void FrontEndCompileRequest::parseTranslationUnit( TranslationUnitRequest* translationUnit) { - IncludeHandlerImpl includeHandler; - auto linkage = getLinkage(); // TODO(JS): NOTE! Here we are using the searchDirectories on the linkage. This is because @@ -986,8 +898,7 @@ void FrontEndCompileRequest::parseTranslationUnit( // If searchDirectories.parent pointed to the one in the Linkage would mean linkage paths // would be checked too (after those on the FrontEndCompileRequest). - includeHandler.linkage = linkage; - includeHandler.searchDirectories = &linkage->searchDirectories; + IncludeHandlerImpl includeHandler(&linkage->searchDirectories, linkage->getFileSystemExt()); RefPtr languageScope; switch (translationUnit->sourceLanguage) @@ -1740,9 +1651,7 @@ RefPtr Linkage::findOrImportModule( // Next, try to find the file of the given name, // using our ordinary include-handling logic. - IncludeHandlerImpl includeHandler; - includeHandler.linkage = this; - includeHandler.searchDirectories = &searchDirectories; + IncludeHandlerImpl includeHandler(&searchDirectories, getFileSystemExt()); // Get the original path info PathInfo pathIncludedFromInfo = getSourceManager()->getPathInfo(loc, SourceLocType::Actual); diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj index 8861413c9..82e575ef9 100644 --- a/source/slang/slang.vcxproj +++ b/source/slang/slang.vcxproj @@ -221,6 +221,7 @@ + @@ -326,6 +327,7 @@ + diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters index a556b5b03..3484a1638 100644 --- a/source/slang/slang.vcxproj.filters +++ b/source/slang/slang.vcxproj.filters @@ -114,6 +114,9 @@ Header Files + + Header Files + Header Files @@ -425,6 +428,9 @@ Source Files + + Source Files + Source Files -- cgit v1.2.3