diff options
38 files changed, 1905 insertions, 562 deletions
diff --git a/.gitignore b/.gitignore index d9280b3d4..3307bd9e1 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,7 @@ tools/gfx-unit-test/test-tmp-* # Intermediate source files generated during build process /source/slang/slang-generated-*.h +/source/slang/slang-lookup-capability-defs.cpp /source/slang/*.meta.slang.h prelude/*.h.cpp /source/slang/cpp.hint diff --git a/CMakeLists.txt b/CMakeLists.txt index c2bd54565..eef229ee6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,12 +256,13 @@ generator(tools/slang-cpp-extractor USE_FEWER_WARNINGS LINK_WITH_PRIVATE compile generator(tools/slang-embed) generator(tools/slang-generate USE_FEWER_WARNINGS) generator(tools/slang-lookup-generator LINK_WITH_PRIVATE compiler-core) +generator(tools/slang-capability-generator LINK_WITH_PRIVATE compiler-core) generator(tools/slang-spirv-embed-generator LINK_WITH_PRIVATE compiler-core) generator( source/slangc TARGET_NAME slang-bootstrap USE_FEWER_WARNINGS - LINK_WITH_PRIVATE prelude slang-no-embedded-stdlib Threads::Threads + LINK_WITH_PRIVATE prelude slang-no-embedded-stdlib slang-capability-lookup Threads::Threads ) # @@ -276,7 +277,7 @@ add_subdirectory(source/slang) slang_add_target( tools/slangd EXECUTABLE - LINK_WITH_PRIVATE core slang slang-reflect-headers Threads::Threads + LINK_WITH_PRIVATE core slang slang-reflect-headers slang-capability-defs Threads::Threads INSTALL ) slang_add_target( diff --git a/build/visual-studio/compiler-core/compiler-core.vcxproj b/build/visual-studio/compiler-core/compiler-core.vcxproj index e2202aaca..22a19abb9 100644 --- a/build/visual-studio/compiler-core/compiler-core.vcxproj +++ b/build/visual-studio/compiler-core/compiler-core.vcxproj @@ -306,6 +306,7 @@ <ClInclude Include="..\..\..\source\compiler-core\slang-name-convention-util.h" />
<ClInclude Include="..\..\..\source\compiler-core\slang-name.h" />
<ClInclude Include="..\..\..\source\compiler-core\slang-nvrtc-compiler.h" />
+ <ClInclude Include="..\..\..\source\compiler-core\slang-perfect-hash-codegen.h" />
<ClInclude Include="..\..\..\source\compiler-core\slang-perfect-hash.h" />
<ClInclude Include="..\..\..\source\compiler-core\slang-slice-allocator.h" />
<ClInclude Include="..\..\..\source\compiler-core\slang-source-embed-util.h" />
@@ -354,6 +355,7 @@ <ClCompile Include="..\..\..\source\compiler-core\slang-name-convention-util.cpp" />
<ClCompile Include="..\..\..\source\compiler-core\slang-name.cpp" />
<ClCompile Include="..\..\..\source\compiler-core\slang-nvrtc-compiler.cpp" />
+ <ClCompile Include="..\..\..\source\compiler-core\slang-perfect-hash-codegen.cpp" />
<ClCompile Include="..\..\..\source\compiler-core\slang-perfect-hash.cpp" />
<ClCompile Include="..\..\..\source\compiler-core\slang-slice-allocator.cpp" />
<ClCompile Include="..\..\..\source\compiler-core\slang-source-embed-util.cpp" />
diff --git a/build/visual-studio/compiler-core/compiler-core.vcxproj.filters b/build/visual-studio/compiler-core/compiler-core.vcxproj.filters index 2d4e3663d..eb1d52dfb 100644 --- a/build/visual-studio/compiler-core/compiler-core.vcxproj.filters +++ b/build/visual-studio/compiler-core/compiler-core.vcxproj.filters @@ -132,6 +132,9 @@ <ClInclude Include="..\..\..\source\compiler-core\slang-nvrtc-compiler.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\compiler-core\slang-perfect-hash-codegen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\compiler-core\slang-perfect-hash.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -272,6 +275,9 @@ <ClCompile Include="..\..\..\source\compiler-core\slang-nvrtc-compiler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\compiler-core\slang-perfect-hash-codegen.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\compiler-core\slang-perfect-hash.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/build/visual-studio/run-generators/run-generators.vcxproj b/build/visual-studio/run-generators/run-generators.vcxproj index 8a143c391..dbd3a3947 100644 --- a/build/visual-studio/run-generators/run-generators.vcxproj +++ b/build/visual-studio/run-generators/run-generators.vcxproj @@ -319,6 +319,23 @@ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../../bin/windows-x64/release/slang-cpp-extractor.exe;../../../source/slang/slang-ast-support-types.h;../../../source/slang/slang-ast-base.h;../../../source/slang/slang-ast-decl.h;../../../source/slang/slang-ast-expr.h;../../../source/slang/slang-ast-modifier.h;../../../source/slang/slang-ast-stmt.h;../../../source/slang/slang-ast-type.h;../../../source/slang/slang-ast-val.h</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release aarch64|ARM64'">../../../bin/windows-aarch64/release/slang-cpp-extractor.exe;../../../source/slang/slang-ast-support-types.h;../../../source/slang/slang-ast-base.h;../../../source/slang/slang-ast-decl.h;../../../source/slang/slang-ast-expr.h;../../../source/slang/slang-ast-modifier.h;../../../source/slang/slang-ast-stmt.h;../../../source/slang/slang-ast-type.h;../../../source/slang/slang-ast-val.h</AdditionalInputs>
</CustomBuild>
+ <CustomBuild Include="..\..\..\source\slang\slang-capabilities.capdef">
+ <FileType>Document</FileType>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"../../../bin/windows-x86/debug/slang-capability-generator" "%(FullPath)"</Command>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"../../../bin/windows-x64/debug/slang-capability-generator" "%(FullPath)"</Command>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug aarch64|ARM64'">"../../../bin/windows-aarch64/debug/slang-capability-generator" "%(FullPath)"</Command>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"../../../bin/windows-x86/release/slang-capability-generator" "%(FullPath)"</Command>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"../../../bin/windows-x64/release/slang-capability-generator" "%(FullPath)"</Command>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release aarch64|ARM64'">"../../../bin/windows-aarch64/release/slang-capability-generator" "%(FullPath)"</Command>
+ <Outputs>../../../source/slang/slang-generated-capability-defs.h</Outputs>
+ <Message>slang-capability-generator %(Identity)</Message>
+ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../../source/slang/slang-capabilities.capdef;../../../bin/windows-x86/debug/slang-capability-generator.exe</AdditionalInputs>
+ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../../source/slang/slang-capabilities.capdef;../../../bin/windows-x64/debug/slang-capability-generator.exe</AdditionalInputs>
+ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug aarch64|ARM64'">../../../source/slang/slang-capabilities.capdef;../../../bin/windows-aarch64/debug/slang-capability-generator.exe</AdditionalInputs>
+ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../../source/slang/slang-capabilities.capdef;../../../bin/windows-x86/release/slang-capability-generator.exe</AdditionalInputs>
+ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../../source/slang/slang-capabilities.capdef;../../../bin/windows-x64/release/slang-capability-generator.exe</AdditionalInputs>
+ <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release aarch64|ARM64'">../../../source/slang/slang-capabilities.capdef;../../../bin/windows-aarch64/release/slang-capability-generator.exe</AdditionalInputs>
+ </CustomBuild>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
diff --git a/build/visual-studio/run-generators/run-generators.vcxproj.filters b/build/visual-studio/run-generators/run-generators.vcxproj.filters index 12c02fd18..c3c8330a8 100644 --- a/build/visual-studio/run-generators/run-generators.vcxproj.filters +++ b/build/visual-studio/run-generators/run-generators.vcxproj.filters @@ -7,6 +7,12 @@ <Filter Include="Source Files">
<UniqueIdentifier>{E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6}</UniqueIdentifier>
</Filter>
+ <Filter Include="source">
+ <UniqueIdentifier>{76FF3A1C-62A1-65F2-4B5C-7946379DEC42}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="source\slang">
+ <UniqueIdentifier>{9A9B9613-86FE-25B8-2F84-5E6E1BC6F13A}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\prelude\slang-cpp-scalar-intrinsics.h">
@@ -58,5 +64,8 @@ <CustomBuild Include="..\..\..\source\slang\slang-ast-reflect.h">
<Filter>Header Files</Filter>
</CustomBuild>
+ <CustomBuild Include="..\..\..\source\slang\slang-capabilities.capdef">
+ <Filter>source\slang</Filter>
+ </CustomBuild>
</ItemGroup>
</Project>
\ No newline at end of file diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj index 30fe5d1bb..123bc734d 100644 --- a/build/visual-studio/slang/slang.vcxproj +++ b/build/visual-studio/slang/slang.vcxproj @@ -325,7 +325,6 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla <ClInclude Include="..\..\..\source\slang\slang-ast-synthesis.h" />
<ClInclude Include="..\..\..\source\slang\slang-ast-type.h" />
<ClInclude Include="..\..\..\source\slang\slang-ast-val.h" />
- <ClInclude Include="..\..\..\source\slang\slang-capability-defs.h" />
<ClInclude Include="..\..\..\source\slang\slang-capability.h" />
<ClInclude Include="..\..\..\source\slang\slang-check-impl.h" />
<ClInclude Include="..\..\..\source\slang\slang-check.h" />
@@ -347,6 +346,8 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla <ClInclude Include="..\..\..\source\slang\slang-emit-spirv-ops-debug-info-ext.h" />
<ClInclude Include="..\..\..\source\slang\slang-emit-spirv-ops.h" />
<ClInclude Include="..\..\..\source\slang\slang-emit-torch.h" />
+ <ClInclude Include="..\..\..\source\slang\slang-generated-capability-defs-impl.h" />
+ <ClInclude Include="..\..\..\source\slang\slang-generated-capability-defs.h" />
<ClInclude Include="..\..\..\source\slang\slang-glsl-extension-tracker.h" />
<ClInclude Include="..\..\..\source\slang\slang-hlsl-to-vulkan-layout-options.h" />
<ClInclude Include="..\..\..\source\slang\slang-image-format-defs.h" />
@@ -698,6 +699,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla <ClCompile Include="..\..\..\source\slang\slang-language-server-semantic-tokens.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-language-server.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-legalize-types.cpp" />
+ <ClCompile Include="..\..\..\source\slang\slang-lookup-capability-defs.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-lookup-glslstd450.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-lookup.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-lower-to-ir.cpp" />
diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters index 320886c18..9f91b13f1 100644 --- a/build/visual-studio/slang/slang.vcxproj.filters +++ b/build/visual-studio/slang/slang.vcxproj.filters @@ -63,9 +63,6 @@ <ClInclude Include="..\..\..\source\slang\slang-ast-val.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\..\..\source\slang\slang-capability-defs.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\..\..\source\slang\slang-capability.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -129,6 +126,12 @@ <ClInclude Include="..\..\..\source\slang\slang-emit-torch.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\slang\slang-generated-capability-defs-impl.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\source\slang\slang-generated-capability-defs.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\slang\slang-glsl-extension-tracker.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -1178,6 +1181,9 @@ <ClCompile Include="..\..\..\source\slang\slang-legalize-types.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\slang\slang-lookup-capability-defs.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\slang\slang-lookup-glslstd450.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/premake5.lua b/premake5.lua index 13c6279c6..51ddbd62c 100644 --- a/premake5.lua +++ b/premake5.lua @@ -999,6 +999,12 @@ tool "slang-lookup-generator" links { "compiler-core", "core" } +tool "slang-capability-generator" + uuid "FD16CA29-C66A-430A-822C-C09655088611" + includedirs { "." } + + links { "compiler-core", "core" } + tool "test-process" uuid "BE412850-4BB9-429A-877C-BFBC4B34186C" includedirs { "." } @@ -1400,6 +1406,26 @@ function preludeGenerator() buildinputs { builddir .. "/slang-embed" .. getExecutableSuffix() } end +function capabilityGenerator() + filter("files:source/slang/*.capdef") + + dependson { "slang-capability-generator" } + local inputFile = "%{file.abspath}" + local builddir = getBuildDir() + local outputHeaderFile = "%{wks.location}/source/slang/slang-generated-capability-defs.h" + local outputCppFile = "%{wks.location}/source/slang/slang-generated-capability-defs-impl.h" + local outputLookupFile = "%{wks.location}/source/slang/slang-lookup-capability-defs.cpp" + if executeBinary then + buildmessage ("slang-capability-generator %{file.relpath}") + local buildcmd = '"' .. builddir .. '/slang-capability-generator" "%{file.abspath}"' + buildcommands { buildcmd } + buildinputs { "%{file.abspath}", builddir .. "/slang-capability-generator" .. getExecutableSuffix() } + buildoutputs (outputHeaderFile, outputCppFile, outputLookupFile) + end + + filter { } +end + if not skipSourceGeneration then generatorProject("run-generators", nil) @@ -1415,6 +1441,7 @@ generatorProject("run-generators", nil) "source/slang/*.meta.slang", -- The stdlib files "source/slang/slang-ast-reflect.h", -- C++ reflection "prelude/*.h", -- The prelude files + "source/slang/*.capdef", -- -- To build we need to have some source! It has to be a source file that @@ -1428,7 +1455,7 @@ generatorProject("run-generators", nil) -- First, we need to ensure that various source-generation tools -- get built before `slang`, so we declare a non-linking dependency between -- the projects here: - dependson { "slang-cpp-extractor", "slang-generate", "slang-embed" } + dependson { "slang-cpp-extractor", "slang-generate", "slang-embed", "slang-capability-generator" } local executableSuffix = getExecutableSuffix() @@ -1446,6 +1473,7 @@ generatorProject("run-generators", nil) if executeBinary then metaSlangGenerator() preludeGenerator() + capabilityGenerator() end filter { } @@ -1564,7 +1592,8 @@ if enableEmbedStdLib then "prelude/slang-hlsl-prelude.h.cpp", "prelude/slang-cpp-prelude.h.cpp", "prelude/slang-cpp-host-prelude.h.cpp", - "prelude/slang-torch-prelude.h.cpp" + "prelude/slang-torch-prelude.h.cpp", + "source/slang/slang-lookup-capability-defs.cpp" } if not targetInfo.isWindows then links { "pthread" } @@ -1684,6 +1713,9 @@ standardProject("slang", "source/slang") -- Similarly for any generated lookup tables files { "source/slang/slang-lookup-glslstd450.cpp", + "source/slang/slang-lookup-capability-defs.cpp", + "source/slang/slang-generated-capability-defs.h", + "source/slang/slang-generated-capability-defs-impl.h", } -- @@ -1695,6 +1727,7 @@ standardProject("slang", "source/slang") if not skipSourceGeneration then dependson { "run-generators" } dependson { "generate-lookup-tables" } + dependson { "generate-capabilities" } dependson { "generate-spirv-embed" } end @@ -17,6 +17,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gfx-util", "build\visual-st EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "platform", "build\visual-studio\platform\platform.vcxproj", "{3565FE5E-4FA3-11EB-AE93-0242AC130002}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-capability-generator", "build\visual-studio\slang-capability-generator\slang-capability-generator.vcxproj", "{FD16CA29-C66A-430A-822C-C09655088611}"
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-cpp-extractor", "build\visual-studio\slang-cpp-extractor\slang-cpp-extractor.vcxproj", "{CA8A30D1-8FA9-4330-B7F7-84709246D8DC}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "slang-embed", "build\visual-studio\slang-embed\slang-embed.vcxproj", "{7F773DD9-EB8F-2403-B43C-B49C2014B99C}"
@@ -84,6 +86,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "run-generators", "build\vis {CA8A30D1-8FA9-4330-B7F7-84709246D8DC} = {CA8A30D1-8FA9-4330-B7F7-84709246D8DC}
{66174227-8541-41FC-A6DF-4764FC66F78E} = {66174227-8541-41FC-A6DF-4764FC66F78E}
{7F773DD9-EB8F-2403-B43C-B49C2014B99C} = {7F773DD9-EB8F-2403-B43C-B49C2014B99C}
+ {FD16CA29-C66A-430A-822C-C09655088611} = {FD16CA29-C66A-430A-822C-C09655088611}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lz4", "build\visual-studio\lz4\lz4.vcxproj", "{E1EC8075-823E-46E5-BC38-C124CCCDF878}"
@@ -169,6 +172,18 @@ Global {3565FE5E-4FA3-11EB-AE93-0242AC130002}.Release|Win32.Build.0 = Release|Win32
{3565FE5E-4FA3-11EB-AE93-0242AC130002}.Release|x64.ActiveCfg = Release|x64
{3565FE5E-4FA3-11EB-AE93-0242AC130002}.Release|x64.Build.0 = Release|x64
+ {FD16CA29-C66A-430A-822C-C09655088611}.Debug|aarch64.ActiveCfg = Debug aarch64|ARM64
+ {FD16CA29-C66A-430A-822C-C09655088611}.Debug|aarch64.Build.0 = Debug aarch64|ARM64
+ {FD16CA29-C66A-430A-822C-C09655088611}.Debug|Win32.ActiveCfg = Debug|Win32
+ {FD16CA29-C66A-430A-822C-C09655088611}.Debug|Win32.Build.0 = Debug|Win32
+ {FD16CA29-C66A-430A-822C-C09655088611}.Debug|x64.ActiveCfg = Debug|x64
+ {FD16CA29-C66A-430A-822C-C09655088611}.Debug|x64.Build.0 = Debug|x64
+ {FD16CA29-C66A-430A-822C-C09655088611}.Release|aarch64.ActiveCfg = Release aarch64|ARM64
+ {FD16CA29-C66A-430A-822C-C09655088611}.Release|aarch64.Build.0 = Release aarch64|ARM64
+ {FD16CA29-C66A-430A-822C-C09655088611}.Release|Win32.ActiveCfg = Release|Win32
+ {FD16CA29-C66A-430A-822C-C09655088611}.Release|Win32.Build.0 = Release|Win32
+ {FD16CA29-C66A-430A-822C-C09655088611}.Release|x64.ActiveCfg = Release|x64
+ {FD16CA29-C66A-430A-822C-C09655088611}.Release|x64.Build.0 = Release|x64
{CA8A30D1-8FA9-4330-B7F7-84709246D8DC}.Debug|aarch64.ActiveCfg = Debug aarch64|ARM64
{CA8A30D1-8FA9-4330-B7F7-84709246D8DC}.Debug|aarch64.Build.0 = Debug aarch64|ARM64
{CA8A30D1-8FA9-4330-B7F7-84709246D8DC}.Debug|Win32.ActiveCfg = Debug|Win32
@@ -610,6 +625,7 @@ Global {222F7498-B40C-4F3F-A704-DDEB91A4484A} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13}
{F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13}
{3565FE5E-4FA3-11EB-AE93-0242AC130002} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13}
+ {FD16CA29-C66A-430A-822C-C09655088611} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13}
{CA8A30D1-8FA9-4330-B7F7-84709246D8DC} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13}
{7F773DD9-EB8F-2403-B43C-B49C2014B99C} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13}
{66174227-8541-41FC-A6DF-4764FC66F78E} = {FD47AE19-69FD-260F-F2F1-20E65EA61D13}
diff --git a/source/compiler-core/slang-perfect-hash-codegen.cpp b/source/compiler-core/slang-perfect-hash-codegen.cpp new file mode 100644 index 000000000..611b34c54 --- /dev/null +++ b/source/compiler-core/slang-perfect-hash-codegen.cpp @@ -0,0 +1,81 @@ +#include "slang-perfect-hash-codegen.h" +#include "../core/slang-io.h" + +namespace Slang +{ + SlangResult writeHashFile( + String outCppPath, + String valueType, + const List<String> includes, + const HashParams& hashParams, + const List<String> values) + { + StringBuilder sb; + StringWriter writer(&sb, WriterFlags(0)); + WriterHelper w(&writer); + + w.print("// Hash function for %s\n", valueType.getBuffer()); + w.print("//\n"); + w.print("// This file was thoughtfully generated by a machine,\n"); + w.print("// don't even think about modifying it yourself!\n"); + w.print("//\n"); + w.print("\n"); + for (const auto& i : includes) + { + if (i.getLength()) + w.print("#include \"%s\"\n", i.getBuffer()); + } + w.print("\n"); + w.print("\n"); + w.print("namespace Slang\n"); + w.print("{\n"); + w.print("\n"); + + w.put(perfectHashToEmbeddableCpp( + hashParams, + valueType.getUnownedSlice(), + (String("lookup") + valueType).getUnownedSlice(), + values + ).getBuffer()); + + w.print("}\n"); + + return File::writeAllTextIfChanged(outCppPath, sb.getUnownedSlice()); + } + + SlangResult writePerfectHashLookupCppFile(String fileName, List<String> opnames, String enumName, String enumerantPrefix, String enumHeaderFile, DiagnosticSink* sink) + { + HashParams hashParams; + auto r = minimalPerfectHash(opnames, hashParams); + switch (r) + { + case HashFindResult::UnavoidableHashCollision: + { + sink->diagnoseRaw( + Severity::Error, + "Unable to find a non-overlapping hash function.\n" + "The hash function probably has a unavoidable " + "collision for some input words\n"); + return SLANG_FAIL; + } + case HashFindResult::NonUniqueKeys: + { + sink->diagnoseRaw(Severity::Error, "Input word list has duplicates\n"); + return SLANG_FAIL; + } + case HashFindResult::Success:; + } + + List<String> values; + values.reserve(hashParams.destTable.getCount()); + for (const auto& v : hashParams.destTable) + values.add(enumerantPrefix + v); + return writeHashFile( + fileName, + enumName, + { "../core/slang-common.h", "../core/slang-string.h", enumHeaderFile }, + hashParams, + values); + } + +} diff --git a/source/compiler-core/slang-perfect-hash-codegen.h b/source/compiler-core/slang-perfect-hash-codegen.h new file mode 100644 index 000000000..557f4dba5 --- /dev/null +++ b/source/compiler-core/slang-perfect-hash-codegen.h @@ -0,0 +1,9 @@ +#pragma once + +#include "slang-perfect-hash.h" +#include "slang-diagnostic-sink.h" + +namespace Slang +{ + SlangResult writePerfectHashLookupCppFile(String fileName, List<String> opnames, String enumName, String enumPrefix, String enumHeaderFile, DiagnosticSink* sink); +} diff --git a/source/compiler-core/slang-perfect-hash.cpp b/source/compiler-core/slang-perfect-hash.cpp index bfe8d4876..d5c4902ed 100644 --- a/source/compiler-core/slang-perfect-hash.cpp +++ b/source/compiler-core/slang-perfect-hash.cpp @@ -208,7 +208,7 @@ String perfectHashToEmbeddableCpp( line("}"); line(""); - return sb; + return sb.produceString(); } } diff --git a/source/slang/CMakeLists.txt b/source/slang/CMakeLists.txt index c3ea18433..44785559f 100644 --- a/source/slang/CMakeLists.txt +++ b/source/slang/CMakeLists.txt @@ -47,6 +47,54 @@ target_include_directories( ) # +# generate capability files +# +glob_append(SLANG_CAPABILITY_SOURCE "*.capdef") + +set(SLANG_CAPABILITY_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/capability") + +# Generate the output file list +set(SLANG_CAPABILITY_GENERATED_FILES + "${SLANG_CAPABILITY_OUTPUT_DIR}/slang-generated-capability-defs.h" + "${SLANG_CAPABILITY_OUTPUT_DIR}/slang-generated-capability-defs-impl.h" + "${SLANG_CAPABILITY_OUTPUT_DIR}/slang-lookup-capability-defs.cpp" +) +add_custom_command( + OUTPUT ${SLANG_CAPABILITY_GENERATED_FILES} + COMMAND ${CMAKE_COMMAND} -E make_directory ${SLANG_CAPABILITY_OUTPUT_DIR} + COMMAND + slang-capability-generator ${SLANG_CAPABILITY_SOURCE} --target-directory ${SLANG_CAPABILITY_OUTPUT_DIR} + DEPENDS ${SLANG_CAPABILITY_SOURCE} + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + VERBATIM +) +add_library( + slang-capability-defs + INTERFACE + EXCLUDE_FROM_ALL + ${SLANG_CAPABILITY_GENERATED_FILES} +) +set_target_properties(slang-capability-defs PROPERTIES FOLDER generated) +target_include_directories( + slang-capability-defs + INTERFACE ${SLANG_CAPABILITY_OUTPUT_DIR} + INTERFACE ${slang_SOURCE_DIR}/source/slang +) + +add_library( + slang-capability-lookup + INTERFACE + EXCLUDE_FROM_ALL + ${SLANG_CAPABILITY_GENERATED_FILES} +) +set_target_properties(slang-capability-lookup PROPERTIES FOLDER generated) +target_sources( + slang-capability-lookup + INTERFACE + "${SLANG_CAPABILITY_OUTPUT_DIR}/slang-lookup-capability-defs.cpp" +) + +# # generated headers for reflection # @@ -118,6 +166,8 @@ slang_add_target( core compiler-core # Bundle the source unconditionally + slang-capability-defs + slang-capability-lookup slang-meta-headers slang-reflect-headers SPIRV-Headers @@ -164,6 +214,8 @@ slang_add_target( core compiler-core prelude + slang-capability-defs + slang-capability-lookup slang-reflect-headers SPIRV-Headers # slang.h is in the project root, so include that directory in the interface diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 2f4064c97..3635069e7 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -4566,8 +4566,8 @@ T GetAttributeAtVertex(T attribute, uint vertexIndex) { case hlsl: __intrinsic_asm "GetAttributeAtVertex"; - case GL_NV_fragment_shader_barycentric: - case GL_EXT_fragment_shader_barycentric: + case _GL_NV_fragment_shader_barycentric: + case _GL_EXT_fragment_shader_barycentric: __intrinsic_asm "$0[$1]"; case spirv: return spirv_asm { @@ -4599,8 +4599,8 @@ vector<T,N> GetAttributeAtVertex(vector<T,N> attribute, uint vertexIndex) { case hlsl: __intrinsic_asm "GetAttributeAtVertex"; - case GL_NV_fragment_shader_barycentric: - case GL_EXT_fragment_shader_barycentric: + case _GL_NV_fragment_shader_barycentric: + case _GL_EXT_fragment_shader_barycentric: __intrinsic_asm "$0[$1]"; case spirv: return spirv_asm { @@ -4632,8 +4632,8 @@ matrix<T,N,M> GetAttributeAtVertex(matrix<T,N,M> attribute, uint vertexIndex) { case hlsl: __intrinsic_asm "GetAttributeAtVertex"; - case GL_NV_fragment_shader_barycentric: - case GL_EXT_fragment_shader_barycentric: + case _GL_NV_fragment_shader_barycentric: + case _GL_EXT_fragment_shader_barycentric: __intrinsic_asm "$0[$1]"; case spirv: return spirv_asm { @@ -9312,8 +9312,8 @@ bool __reportIntersection(float tHit, uint hitKind) { __target_switch { - case GL_EXT_ray_tracing: __intrinsic_asm "reportIntersectionEXT"; - case GL_NV_ray_tracing: __intrinsic_asm "reportIntersectionNV"; + case _GL_EXT_ray_tracing: __intrinsic_asm "reportIntersectionEXT"; + case _GL_NV_ray_tracing: __intrinsic_asm "reportIntersectionNV"; case spirv: return spirv_asm { result:$$bool = OpReportIntersectionKHR $tHit $hitKind; @@ -9339,8 +9339,8 @@ void IgnoreHit() __target_switch { case hlsl: __intrinsic_asm "IgnoreHit"; - case GL_EXT_ray_tracing: __intrinsic_asm "ignoreIntersectionEXT;"; - case GL_NV_ray_tracing: __intrinsic_asm "ignoreIntersectionNV"; + case _GL_EXT_ray_tracing: __intrinsic_asm "ignoreIntersectionEXT;"; + case _GL_NV_ray_tracing: __intrinsic_asm "ignoreIntersectionNV"; case cuda: __intrinsic_asm "optixIgnoreIntersection"; case spirv: spirv_asm { OpIgnoreIntersectionKHR; %_ = OpLabel }; } @@ -9352,8 +9352,8 @@ void AcceptHitAndEndSearch() __target_switch { case hlsl: __intrinsic_asm "AcceptHitAndEndSearch"; - case GL_EXT_ray_tracing: __intrinsic_asm "terminateRayEXT;"; - case GL_NV_ray_tracing: __intrinsic_asm "terminateRayNV"; + case _GL_EXT_ray_tracing: __intrinsic_asm "terminateRayEXT;"; + case _GL_NV_ray_tracing: __intrinsic_asm "terminateRayNV"; case cuda: __intrinsic_asm "optixTerminateRay"; case spirv: spirv_asm { OpTerminateRayKHR; %_ = OpLabel }; } @@ -9371,8 +9371,8 @@ uint3 DispatchRaysIndex() __target_switch { case hlsl: __intrinsic_asm "DispatchRaysIndex"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_LaunchIDEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_LaunchIDNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_LaunchIDEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_LaunchIDNV)"; case cuda: __intrinsic_asm "optixGetLaunchIndex"; case spirv: return spirv_asm { @@ -9386,8 +9386,8 @@ uint3 DispatchRaysDimensions() __target_switch { case hlsl: __intrinsic_asm "DispatchRaysDimensions"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_LaunchSizeEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_LaunchSizeNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_LaunchSizeEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_LaunchSizeNV)"; case cuda: __intrinsic_asm "optixGetLaunchDimensions"; case spirv: return spirv_asm { @@ -9403,8 +9403,8 @@ float3 WorldRayOrigin() __target_switch { case hlsl: __intrinsic_asm "WorldRayOrigin"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldRayOriginEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldRayOriginNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldRayOriginEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldRayOriginNV)"; case cuda: __intrinsic_asm "optixGetWorldRayOrigin"; case spirv: return spirv_asm { @@ -9418,8 +9418,8 @@ float3 WorldRayDirection() __target_switch { case hlsl: __intrinsic_asm "WorldRayDirection"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldRayDirectionEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldRayDirectionNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldRayDirectionEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldRayDirectionNV)"; case cuda: __intrinsic_asm "optixGetWorldRayDirection"; case spirv: return spirv_asm { @@ -9433,8 +9433,8 @@ float RayTMin() __target_switch { case hlsl: __intrinsic_asm "RayTMin"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_RayTminEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_RayTminNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_RayTminEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_RayTminNV)"; case cuda: __intrinsic_asm "optixGetRayTmin"; case spirv: return spirv_asm { @@ -9458,8 +9458,8 @@ float RayTCurrent() __target_switch { case hlsl: __intrinsic_asm "RayTCurrent"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_RayTmaxEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_RayTmaxNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_RayTmaxEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_RayTmaxNV)"; case cuda: __intrinsic_asm "optixGetRayTmax"; case spirv: return spirv_asm { @@ -9473,8 +9473,8 @@ uint RayFlags() __target_switch { case hlsl: __intrinsic_asm "RayFlags"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_IncomingRayFlagsEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_IncomingRayFlagsNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_IncomingRayFlagsEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_IncomingRayFlagsNV)"; case cuda: __intrinsic_asm "optixGetRayFlags"; case spirv: return spirv_asm { @@ -9490,7 +9490,7 @@ uint InstanceIndex() __target_switch { case hlsl: __intrinsic_asm "InstanceIndex"; - case __glslRayTracing: __intrinsic_asm "(gl_InstanceID)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_InstanceID)"; case cuda: __intrinsic_asm "optixGetInstanceIndex"; case spirv: return spirv_asm { @@ -9504,8 +9504,8 @@ uint InstanceID() __target_switch { case hlsl: __intrinsic_asm "InstanceID"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_InstanceCustomIndexEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_InstanceCustomIndexNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_InstanceCustomIndexEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_InstanceCustomIndexNV)"; case cuda: __intrinsic_asm "optixGetInstanceId"; case spirv: return spirv_asm { @@ -9519,7 +9519,7 @@ uint PrimitiveIndex() __target_switch { case hlsl: __intrinsic_asm "PrimitiveIndex"; - case __glslRayTracing: __intrinsic_asm "(gl_PrimitiveID)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_PrimitiveID)"; case cuda: __intrinsic_asm "optixGetPrimitiveIndex"; case spirv: return spirv_asm { @@ -9533,8 +9533,8 @@ float3 ObjectRayOrigin() __target_switch { case hlsl: __intrinsic_asm "ObjectRayOrigin"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectRayOriginEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectRayOriginNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectRayOriginEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectRayOriginNV)"; case cuda: __intrinsic_asm "optixGetObjectRayOrigin"; case spirv: return spirv_asm { @@ -9548,8 +9548,8 @@ float3 ObjectRayDirection() __target_switch { case hlsl: __intrinsic_asm "ObjectRayDirection"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectRayDirectionEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectRayDirectionNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectRayDirectionEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectRayDirectionNV)"; case cuda: __intrinsic_asm "optixGetObjectRayDirection"; case spirv: return spirv_asm { @@ -9565,8 +9565,8 @@ float3x4 ObjectToWorld3x4() __target_switch { case hlsl: __intrinsic_asm "ObjectToWorld3x4"; - case GL_EXT_ray_tracing: __intrinsic_asm "transpose(gl_ObjectToWorldEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "transpose(gl_ObjectToWorldNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "transpose(gl_ObjectToWorldEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "transpose(gl_ObjectToWorldNV)"; case spirv: return spirv_asm { %mat:$$float4x3 = OpLoad builtin(ObjectToWorldKHR:float4x3); @@ -9580,8 +9580,8 @@ float3x4 WorldToObject3x4() __target_switch { case hlsl: __intrinsic_asm "WorldToObject3x4"; - case GL_EXT_ray_tracing: __intrinsic_asm "transpose(gl_WorldToObjectEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "transpose(gl_WorldToObjectNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "transpose(gl_WorldToObjectEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "transpose(gl_WorldToObjectNV)"; case spirv: return spirv_asm { %mat:$$float4x3 = OpLoad builtin(WorldToObjectKHR:float4x3); @@ -9595,8 +9595,8 @@ float4x3 ObjectToWorld4x3() __target_switch { case hlsl: __intrinsic_asm "ObjectToWorld4x3"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectToWorldEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectToWorldNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_ObjectToWorldEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_ObjectToWorldNV)"; case spirv: return spirv_asm { result:$$float4x3 = OpLoad builtin(ObjectToWorldKHR:float4x3); @@ -9609,8 +9609,8 @@ float4x3 WorldToObject4x3() __target_switch { case hlsl: __intrinsic_asm "WorldToObject4x3"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldToObjectEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldToObjectNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_WorldToObjectEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_WorldToObjectNV)"; case spirv: return spirv_asm { result:$$float4x3 = OpLoad builtin(WorldToObjectKHR:float4x3); @@ -9656,8 +9656,8 @@ uint HitKind() __target_switch { case hlsl: __intrinsic_asm "HitKind"; - case GL_EXT_ray_tracing: __intrinsic_asm "(gl_HitKindEXT)"; - case GL_NV_ray_tracing: __intrinsic_asm "(gl_HitKindNV)"; + case _GL_EXT_ray_tracing: __intrinsic_asm "(gl_HitKindEXT)"; + case _GL_NV_ray_tracing: __intrinsic_asm "(gl_HitKindNV)"; case cuda: __intrinsic_asm "optixGetHitKind"; case spirv: return spirv_asm { @@ -9848,7 +9848,7 @@ extension __TextureImpl<T,__Shape2D, 1, 0, 0, $(kStdlibResourceAccessFeedback), // // Get the index of the geometry that was hit in an intersection, any-hit, or closest-hit shader -__target_intrinsic(GL_EXT_ray_tracing, "(gl_GeometryIndexEXT)") +__glsl_extension(GL_EXT_ray_tracing) uint GeometryIndex() { __target_switch diff --git a/source/slang/slang-capabilities.capdef b/source/slang/slang-capabilities.capdef new file mode 100644 index 000000000..7e5931edf --- /dev/null +++ b/source/slang/slang-capabilities.capdef @@ -0,0 +1,135 @@ +// This file is the single source of truth for all capabilities +// supported by the Slang language. +// +// This file will be parsed and processed by the slang-capability-generator +// tool during the build process to produce slang-generated-capability-defs.h +// and slang-generated-capability-defs-impl.h files that constitute the final +// C++ source of the compiler. New capabilties should be added by editing +// this file instead of the generated .h files. +// +// A capability atom represent a basic unit that characterizes a single code-gen target or +// a platform-specific API/feature, e.g. _GL_EXT_ray_tracing represents the GLSL ray tracing +// extension, and `glsl` represents the GLSL code gen target. +// +// A capability name is defined by a unique disjunction of conjunction of capability atoms. +// For example, `raytracing` is a name that expands to +// glsl + _GL_EXT_ray_tracing | spirv_1_4 + _GL_EXT_ray_tracing | hlsl + _sm_6_4 +// which means it requires the `_GL_EXT_ray_tracing` extension when generating code for glsl +// or spirv, and requires sm_6_4 when generating hlsl. +// +// There are three types of capability definitions: +// - `def`: this will introduce an new capability atom. If there is an inheritance clause, +// the capability name will expand to all inherited atoms plus the newly introduced atom. +// - `abstract`: an abstract capability does not introduce an actual atom, but it defines +// an implicit conflict group such that two capabilities inheriting the same abstract +// capability cannot be satisfied simultaneously. When used in an expression, `abstract` +// capability also expands into a disjunction (or) of all inherited capabilities. +// - `alias`: this defines an alias and does not introduce actual atoms. + +// Several capabilities represent the target formats in which we generate code. +// Because we can only generate code in one format at a time, all of these are +// marked as conflicting with one another along the `TargetFormat` axis. +// +// Note: We are only including here the source code formats we initially generate +// code in and not the formats that code might be translated into "downstream." +// Trying to figure out how to integrate both kinds of formats into our capability +// system will be an interesting challenge (e.g., can we compile code for `hlsl+spirv` +// and for `glsl+spirv` or even just for `spirv`, and how should all of those impact +// overloading). +// +def textualTarget; + +abstract target; +def hlsl : target + textualTarget; +def glsl : target + textualTarget; +def c : target + textualTarget; +def cpp : target + textualTarget; +def cuda : target + textualTarget; + +// We have multiple capabilities for the various SPIR-V versions, +// arranged so that they inherit from one another to represent which versions +// provide a super-set of the features of earlier ones (e.g., SPIR-V 1.4 is +// expressed as inheriting from SPIR-V 1.3). +// +def spirv_1_0 : target; +def spirv_1_1 : spirv_1_0; +def spirv_1_2 : spirv_1_1; +def spirv_1_3 : spirv_1_2; +def spirv_1_4 : spirv_1_3; +def spirv_1_5 : spirv_1_4; +def spirv_1_6 : spirv_1_5; +alias spirv = spirv_1_0; +alias spirv_latest = spirv_1_6; + +// Capabilities that stand for target spirv version for GLSL backend. +// These are not compilation targets. +def glsl_spirv_1_0; +def glsl_spirv_1_1 : glsl_spirv_1_0; +def glsl_spirv_1_2 : glsl_spirv_1_1; +def glsl_spirv_1_3 : glsl_spirv_1_2; +def glsl_spirv_1_4 : glsl_spirv_1_3; +def glsl_spirv_1_5 : glsl_spirv_1_4; +def glsl_spirv_1_6 : glsl_spirv_1_5; + +abstract stage; +def vertex : stage; +def fragment : stage; +alias pixel = fragment; +def compute : stage; +def hull : stage; +def domain : stage; +def geometry : stage; +def raygen : stage; +def intersection : stage; +def anyhit : stage; +def closesthit: stage; +def miss : stage; +def mesh : stage; + +def _sm_5_1 : hlsl; +def _sm_6_0 : _sm_5_1; +def _sm_6_1 : _sm_6_0; +def _sm_6_2 : _sm_6_1; +def _sm_6_3 : _sm_6_2; +def _sm_6_4 : _sm_6_3; +def _sm_6_5 : _sm_6_4; +def _sm_6_6 : _sm_6_5; +def _sm_6_7 : _sm_6_6; + +// The following capabilities all pertain to how ray tracing shaders are translated +// to GLSL, where there are two different extensions that can provide the core +// functionality of `TraceRay` and the related operations. +// +// The two extensions are expressed as distinct capabilities that both are marked +// as conflicting on the `RayTracingExtension` axis, so that a compilation target +// cannot have both enabled at once. +// +// The `GL_EXT_ray_tracing` extension should be favored, so it has a rank of `1` +// instead of `0`, which means that when comparing overloads that require these +// extensions, the `EXT` extension will be favored over the `NV` extension, if +// all other factors are equal. +// +def _GL_EXT_ray_tracing : glsl + glsl_spirv_1_4 = 1; +def _GL_NV_ray_tracing : _GL_EXT_ray_tracing; +def _SPV_KHR_ray_tracing : spirv_1_4; +alias GL_NV_ray_tracing = _GL_NV_ray_tracing | _SPV_KHR_ray_tracing | _sm_6_4 | cuda; +alias GL_EXT_ray_tracing = _GL_EXT_ray_tracing | _SPV_KHR_ray_tracing | _sm_6_4 | cuda; +alias raytracing = GL_EXT_ray_tracing; + +def _GL_EXT_fragment_shader_barycentric : glsl + fragment; +def _GL_NV_fragment_shader_barycentric : _GL_EXT_fragment_shader_barycentric; +def _SPV_KHR_fragment_shader_barycentric : spirv_1_0 + fragment; +alias GL_NV_fragment_shader_barycentric = _GL_NV_fragment_shader_barycentric | _SPV_KHR_fragment_shader_barycentric | hlsl + fragment; +alias GL_EXT_fragment_shader_barycentric = _GL_EXT_fragment_shader_barycentric | _SPV_KHR_fragment_shader_barycentric | hlsl + fragment; + +// TODO: define what SM means for all supported targets. + +alias sm_5_1 = _sm_5_1; +alias sm_6_0 = _sm_6_0; +alias sm_6_1 = _sm_6_1; +alias sm_6_2 = _sm_6_2; +alias sm_6_3 = _sm_6_3; +alias sm_6_4 = _sm_6_4 | raytracing; +alias sm_6_5 = _sm_6_5; +alias sm_6_6 = _sm_6_6; +alias sm_6_7 = _sm_6_7; diff --git a/source/slang/slang-capability-defs.h b/source/slang/slang-capability-defs.h deleted file mode 100644 index c523d9cda..000000000 --- a/source/slang/slang-capability-defs.h +++ /dev/null @@ -1,103 +0,0 @@ -// slang-capability-defs.h - -// This file uses macros to define the capability "atoms" that -// are used by the `CapabilitySet` implementation. -// -// Any file that `#include`s this file is required to set -// the `SLANG_CAPABILITY_ATOM` macro before including it. -// -#ifndef SLANG_CAPABILITY_ATOM -#error Must define SLANG_CAPABILITY_ATOM before including. -#endif -// -// It is not necessary to `#undef` the macro in the client -// file, because this file will `#undef` it at the end. - -// Our representation allows each capability atom to define -// a number of other base atoms that it "inherits" from. -// -// Different atoms will need different numbers of bases, -// so we will define a few different macros that wrap -// `SLANG_CAPABILITY_ATOM` and let us handle the cases -// more conveniently. -// -// TODO: There is probably a way to handle this with -// variadic macros. -// -#define SLANG_CAPABILITY_ATOM4(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1, BASE2, BASE3) \ - SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1, BASE2, BASE3) - -#define SLANG_CAPABILITY_ATOM3(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1, BASE2) \ - SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1, BASE2, Invalid) - -#define SLANG_CAPABILITY_ATOM2(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1) \ - SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, BASE1, Invalid, Invalid) - -#define SLANG_CAPABILITY_ATOM1(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0) \ - SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, BASE0, Invalid, Invalid, Invalid) - -#define SLANG_CAPABILITY_ATOM0(ENUMERATOR, NAME, KIND, CONFLICTS, RANK) \ - SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, KIND, CONFLICTS, RANK, Invalid, Invalid, Invalid, Invalid) - -// Several capabilities represent the target formats in which we generate code. -// Because we can only generate code in one format at a time, all of these are -// marked as conflicting with one another along the `TargetFormat` axis. -// -// Note: We are only including here the source code formats we initially generate -// code in and not the formats that code might be translated into "downstream." -// Trying to figure out how to integrate both kinds of formats into our capability -// system will be an interesting challenge (e.g., can we compile code for `hlsl+spirv` -// and for `glsl+spirv` or even just for `spirv`, and how should all of those impact -// overloading). -// -SLANG_CAPABILITY_ATOM0(TEXTUAL_SOURCE, textual_source, Abstract,None,0) -SLANG_CAPABILITY_ATOM1(HLSL, hlsl, Concrete,TargetFormat,0,TEXTUAL_SOURCE) -SLANG_CAPABILITY_ATOM1(GLSL, glsl, Concrete,TargetFormat,0,TEXTUAL_SOURCE) -SLANG_CAPABILITY_ATOM1(C, c, Concrete,TargetFormat,0,TEXTUAL_SOURCE) -SLANG_CAPABILITY_ATOM1(CPP, cpp, Concrete,TargetFormat,0,TEXTUAL_SOURCE) -SLANG_CAPABILITY_ATOM1(CUDA, cuda, Concrete,TargetFormat,0,TEXTUAL_SOURCE) - -SLANG_CAPABILITY_ATOM0(SPIRV_DIRECT, spirv, Concrete, TargetFormat, 0) - -// We have multiple capabilities for the various SPIR-V versions, -// arranged so that they inherit from one another to represent which versions -// provide a super-set of the features of earlier ones (e.g., SPIR-V 1.4 is -// expressed as inheriting from SPIR-V 1.3). -// -SLANG_CAPABILITY_ATOM1(SPIRV, __spirv, Abstract,None,0, GLSL) -SLANG_CAPABILITY_ATOM1(SPIRV_1_0, spirv_1_0, Concrete,None,0, SPIRV) -SLANG_CAPABILITY_ATOM1(SPIRV_1_1, spirv_1_1, Concrete,None,0, SPIRV_1_0) -SLANG_CAPABILITY_ATOM1(SPIRV_1_2, spirv_1_2, Concrete,None,0, SPIRV_1_1) -SLANG_CAPABILITY_ATOM1(SPIRV_1_3, spirv_1_3, Concrete,None,0, SPIRV_1_2) -SLANG_CAPABILITY_ATOM1(SPIRV_1_4, spirv_1_4, Concrete,None,0, SPIRV_1_3) -SLANG_CAPABILITY_ATOM1(SPIRV_1_5, spirv_1_5, Concrete,None,0, SPIRV_1_4) - -// The following capabilities all pertain to how ray tracing shaders are translated -// to GLSL, where there are two different extensions that can provide the core -// functionality of `TraceRay` and the related operations. -// -// The two extensions are expressed as distinct capabilities that both are marked -// as conflicting on the `RayTracingExtension` axis, so that a compilation target -// cannot have both enabled at once. -// -// The `GL_EXT_ray_tracing` extension should be favored, so it has a rank of `1` -// instead of `0`, which means that when comparing overloads that require these -// extensions, the `EXT` extension will be favored over the `NV` extension, if -// all other factors are equal. -// -SLANG_CAPABILITY_ATOM1(GLSLRayTracing, __glslRayTracing, Abstract,None,0, GLSL) -SLANG_CAPABILITY_ATOM1(GL_NV_ray_tracing, GL_NV_ray_tracing, Concrete,RayTracingExtension,0, GLSLRayTracing) -SLANG_CAPABILITY_ATOM2(GL_EXT_ray_tracing, GL_EXT_ray_tracing, Concrete,RayTracingExtension,1, GLSLRayTracing, SPIRV_1_4) - -SLANG_CAPABILITY_ATOM1(GLSLFragmentShaderBarycentric, __glslFragmentShaderBarycentric, Abstract, None, 0, GLSL) -SLANG_CAPABILITY_ATOM1(GL_NV_fragment_shader_barycentric, GL_NV_fragment_shader_barycentric, Concrete, FragmentShaderBarycentricExtension, 0, GLSLFragmentShaderBarycentric) -SLANG_CAPABILITY_ATOM1(GL_EXT_fragment_shader_barycentric, GL_EXT_fragment_shader_barycentric, Concrete, FragmentShaderBarycentricExtension, 1, GLSLFragmentShaderBarycentric) - - -#undef SLANG_CAPABILITY_ATOM0 -#undef SLANG_CAPABILITY_ATOM1 -#undef SLANG_CAPABILITY_ATOM2 -#undef SLANG_CAPABILITY_ATOM3 -#undef SLANG_CAPABILITY_ATOM4 - -#undef SLANG_CAPABILITY_ATOM diff --git a/source/slang/slang-capability.cpp b/source/slang/slang-capability.cpp index 28dea8d23..a3f8157e7 100644 --- a/source/slang/slang-capability.cpp +++ b/source/slang/slang-capability.cpp @@ -14,7 +14,7 @@ namespace Slang // We are going to divide capability atoms into a few categories. // -enum class CapabilityAtomFlavor : int32_t +enum class CapabilityNameFlavor : int32_t { // A concrete capability atom is something that a target // can directly support, where the presence of the feature @@ -42,102 +42,68 @@ enum class CapabilityAtomFlavor : int32_t Alias, }; -// Certain capability atoms will conflict with one another, -// such that a concrete target should never be able to support -// both. -// -// It is possible in theory to define "conflicting" capabilities -// in terms of the inheritance graph, but that makes checking -// for conflicts more difficult. -// -// Instead, we are going to allow each capability to define a -// mask to indicate group(s) of conflicting capabilities it -// belongs to. Two different capability atoms that have -// overlapping masks will be considered to conflict. -// -enum class CapabilityAtomConflictMask : uint32_t -{ - // By default, most capability atoms do not conflict with one another. - None = 0, - - // Capability atoms that reprsent target code generation formats always conflict. - // (e.g., you cannot generate both HLSL and C++ output at once) - TargetFormat = 1 << 0, - - // Capability atoms that represent GLSL ray tracing extensions conflict with - // one another (we only want to use one such extension at a time). - RayTracingExtension = 1 << 1, - - // Capability atoms that represent GLSL fragment shader barycentric extensions conflict with - // one another (we only want to use one such extension at a time). - FragmentShaderBarycentricExtension = 1 << 2, -}; - -// For simplicity in building up our data structure representing -// all capability atoms, we will limit the number of bases that -// a capability atom is allowed to inherit from. -// -static const int kCapabilityAtom_MaxBases = 4; - // The macros in the `slang-capability-defs.h` file will be used // to fill out a `static const` array of information about each // capability atom. // struct CapabilityAtomInfo { - /// The API-/language-exposed name of the capability. - char const* name; + /// The API-/language-exposed name of the capability. + char const* name; - /// Flavor of atom: concrete, abstract, or alias - CapabilityAtomFlavor flavor; + /// Flavor of atom: concrete, abstract, or alias + CapabilityNameFlavor flavor; - /// A mask to indicate which other categories of atoms this one conflicts with - CapabilityAtomConflictMask conflictMask; + /// If the atom is a direct descendent of an abstract base, keep that for reference here. + CapabilityName abstractBase; - /// Ranking to use when deciding if this atom is a "better" one to select. + /// Ranking to use when deciding if this atom is a "better" one to select. uint32_t rank; - /// Base atoms this one "inherits" from (terminated with `Invalid` if not all entries used). - CapabilityAtom bases[kCapabilityAtom_MaxBases]; + /// Canonical representation in the form of disjunction-of-conjunction of atoms. + ArrayView<ArrayView<CapabilityName>> canonicalRepresentation; }; -// -static const CapabilityAtomInfo kCapabilityAtoms[Int(CapabilityAtom::Count)] = -{ - { "invalid", CapabilityAtomFlavor::Concrete, CapabilityAtomConflictMask::None, 0, { CapabilityAtom::Invalid, CapabilityAtom::Invalid, CapabilityAtom::Invalid, CapabilityAtom::Invalid } }, -#define SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, FLAVOR, CONFLICT, RANK, BASE0, BASE1, BASE2, BASE3) \ - { #NAME, CapabilityAtomFlavor::FLAVOR, CapabilityAtomConflictMask::CONFLICT, RANK, { CapabilityAtom::BASE0, CapabilityAtom::BASE1, CapabilityAtom::BASE2, CapabilityAtom::BASE3 } }, -#include "slang-capability-defs.h" -}; +#include "slang-generated-capability-defs-impl.h" - /// Get the extended information structure for the given capability `atom` +static CapabilityAtom asAtom(CapabilityName name) +{ + SLANG_ASSERT((CapabilityAtom)name < CapabilityAtom::Count); + return (CapabilityAtom)name; +} + +/// Get the extended information structure for the given capability `atom` +static CapabilityAtomInfo const& _getInfo(CapabilityName atom) +{ + SLANG_ASSERT(Int(atom) < Int(CapabilityName::Count)); + return kCapabilityNameInfos[Int(atom)]; +} static CapabilityAtomInfo const& _getInfo(CapabilityAtom atom) { SLANG_ASSERT(Int(atom) < Int(CapabilityAtom::Count)); - return kCapabilityAtoms[Int(atom)]; + return kCapabilityNameInfos[Int(atom)]; } -void getCapabilityAtomNames(List<UnownedStringSlice>& ioNames) +void getCapabilityNames(List<UnownedStringSlice>& ioNames) { - ioNames.setCount(Count(CapabilityAtom::Count)); - for (Index i = 0; i < Count(CapabilityAtom::Count); ++i) + ioNames.reserve(Count(CapabilityName::Count)); + for (Index i = 0; i < Count(CapabilityName::Count); ++i) { - ioNames[i] = UnownedStringSlice(_getInfo(CapabilityAtom(i)).name); + if (_getInfo(CapabilityName(i)).flavor != CapabilityNameFlavor::Abstract) + { + ioNames.add(UnownedStringSlice(_getInfo(CapabilityName(i)).name)); + } } } -CapabilityAtom findCapabilityAtom(UnownedStringSlice const& name) +bool lookupCapabilityName(const UnownedStringSlice& str, CapabilityName& value); + +CapabilityName findCapabilityName(UnownedStringSlice const& name) { - // For now we are implementing a linear search over the - // array of capability atoms to perform name lookup. - // - for( Index i = 0; i < Index(CapabilityAtom::Count); ++i ) - { - auto& capInfo = _getInfo(CapabilityAtom(i)); - if(name == UnownedTerminatedStringSlice(capInfo.name)) - return CapabilityAtom(i); - } - return CapabilityAtom::Invalid; + CapabilityName result; + if (!lookupCapabilityName(name, result)) + return CapabilityName::Invalid; + return result; } bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base) @@ -147,29 +113,22 @@ bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base) return true; } - const auto& info = kCapabilityAtoms[Index(atom)]; - - for (auto cur : info.bases) + const auto& info = kCapabilityNameInfos[Index(atom)]; + for (auto cur : info.canonicalRepresentation) { - if (cur == CapabilityAtom::Invalid) - { - return false; - } - - if (isCapabilityDerivedFrom(cur, base)) - { - return true; - } + for (auto cbase : cur) + if (asAtom(cbase) == base) + return true; } return false; } // -// CapabilitySet +// CapabilityConjunctionSet // -// The current design choice in `CapabilitySet` is that it stores +// The current design choice in `CapabilityConjunctionSet` is that it stores // an expanded, deduplicated, and sorted list of the capability // atoms in the set. "Expanded" here means that it includes the // transitive closure of the inheritance graph of those atoms. @@ -179,90 +138,64 @@ bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base) // binary searches to efficiently detect whether an atom // is present in a set. -CapabilitySet::CapabilitySet() +CapabilityConjunctionSet::CapabilityConjunctionSet() {} -CapabilitySet::CapabilitySet(Int atomCount, CapabilityAtom const* atoms) +CapabilityConjunctionSet::CapabilityConjunctionSet(Int atomCount, CapabilityAtom const* atoms) { _init(atomCount, atoms); } -CapabilitySet::CapabilitySet(CapabilityAtom atom) +CapabilityConjunctionSet::CapabilityConjunctionSet(CapabilityAtom atom) { _init(1, &atom); } -CapabilitySet::CapabilitySet(List<CapabilityAtom> const& atoms) +CapabilityConjunctionSet::CapabilityConjunctionSet(List<CapabilityAtom> const& atoms) { _init(atoms.getCount(), atoms.getBuffer()); } -CapabilitySet CapabilitySet::makeEmpty() +CapabilityConjunctionSet CapabilityConjunctionSet::makeEmpty() { - return CapabilitySet(); + return CapabilityConjunctionSet(); } -CapabilitySet CapabilitySet::makeInvalid() +CapabilityConjunctionSet CapabilityConjunctionSet::makeInvalid() { // An invalid capability set will always be a singleton // set of the `Invalid` atom, and we will construct // the set directly rather than use the more expensive // logic in `_init()`. // - CapabilitySet result; + CapabilityConjunctionSet result; result.m_expandedAtoms.add(CapabilityAtom::Invalid); return result; } - /// Helper routine for `CapabilitySet::_init`. - /// - /// Recursively add all atoms implied by `atom` to `ioExpandedAtoms`. - /// -static void _addAtomsRec( - CapabilityAtom atom, - HashSet<CapabilityAtom>& ioExpandedAtoms) +void CapabilityConjunctionSet::_init(Int atomCount, CapabilityAtom const* atoms) { - auto& atomInfo = _getInfo(atom); - - // The first step is to add `atom` itself, *unless* - // it is an alias, because an alias shouldn't impact - // whether one set is considered a subset/superset of - // another. - // - if(atomInfo.flavor != CapabilityAtomFlavor::Alias) - { - ioExpandedAtoms.add(atom); - } - - // Next we add all the atoms transitively implied by `atom`. - // - for(auto baseAtom : atomInfo.bases) - { - // Note: the list of `bases` is a fixed-size array, but - // can be terminated with `Invalid` to indicate that - // not all of the entries are being used. - // - // If we see the sentinel, then we know we are at the end - // of the list. - // - if(baseAtom == CapabilityAtom::Invalid) - break; - - _addAtomsRec(baseAtom, ioExpandedAtoms); - } -} - -void CapabilitySet::_init(Int atomCount, CapabilityAtom const* atoms) -{ - // In order to fill in the expanded and deduplicated - // set of atoms, we will use an explicit hash set - // and then recursively walk the tree of atoms and - // their bases. + // We will use an explicit hash set to deduplicate input atoms. // HashSet<CapabilityAtom> expandedAtomsSet; for(Int i = 0; i < atomCount; ++i) { - _addAtomsRec(atoms[i], expandedAtomsSet); + if (expandedAtomsSet.add(atoms[i])) + { + auto& info = _getInfo(atoms[i]); + + // Add the base items that this atom implies. + if (info.canonicalRepresentation.getCount() == 1) + { + // The atom must have only one conjunction. + SLANG_ASSERT(info.canonicalRepresentation.getCount() == 1); + + for (auto base : info.canonicalRepresentation[0]) + { + expandedAtomsSet.add(asAtom(base)); + } + } + } } // We can then translate the set of atoms into a list, @@ -276,7 +209,7 @@ void CapabilitySet::_init(Int atomCount, CapabilityAtom const* atoms) m_expandedAtoms.sort(); } -void CapabilitySet::calcCompactedAtoms(List<CapabilityAtom>& outAtoms) const +void CapabilityConjunctionSet::calcCompactedAtoms(List<CapabilityAtom>& outAtoms) const { // A "compacted" list of atoms is one that starts with // the "expanded" list and removes any atoms that are @@ -294,13 +227,18 @@ void CapabilitySet::calcCompactedAtoms(List<CapabilityAtom>& outAtoms) const for( auto atom : m_expandedAtoms ) { auto& atomInfo = _getInfo(atom); - for(auto baseAtom : atomInfo.bases) + if (atomInfo.canonicalRepresentation.getCount() != 1) { - // Note: dealing with possible early termination of the `bases` list. - if(baseAtom == CapabilityAtom::Invalid) - break; + // If the atom is not a single conjunction, skip. + continue; + } + for(auto baseAtom : atomInfo.canonicalRepresentation[0]) + { + // Note: don't add atom itself into redundant set. + if(asAtom(baseAtom) == atom) + continue; - redundantAtomsSet.add(baseAtom); + redundantAtomsSet.add(asAtom(baseAtom)); } } @@ -318,7 +256,7 @@ void CapabilitySet::calcCompactedAtoms(List<CapabilityAtom>& outAtoms) const } } -bool CapabilitySet::isEmpty() const +bool CapabilityConjunctionSet::isEmpty() const { // Checking if a capability set is empty is trivial in any representation; // all we need to know is if it has zero atoms in its definition. @@ -326,7 +264,7 @@ bool CapabilitySet::isEmpty() const return m_expandedAtoms.getCount() == 0; } -bool CapabilitySet::isInvalid() const +bool CapabilityConjunctionSet::isInvalid() const { // We will assume here that there is only one canonical representation of // an invalid capability set, which is a singleton set of the `Invalid` @@ -342,32 +280,47 @@ bool CapabilitySet::isInvalid() const return m_expandedAtoms[0] == CapabilityAtom::Invalid; } -bool CapabilitySet::isIncompatibleWith(CapabilityAtom that) const +bool CapabilityConjunctionSet::isIncompatibleWith(CapabilityAtom that) const { // Checking for incompatibility is complicated, and it is best // to only implement it for full (expanded) sets. // - return isIncompatibleWith(CapabilitySet(that)); + return isIncompatibleWith(CapabilityConjunctionSet(that)); } -uint32_t CapabilitySet::_calcConflictMask() const +static UIntSet _calcConflictMask(CapabilityAtom atom) +{ + UIntSet mask; + auto abstractBase = _getInfo(atom).abstractBase; + if (abstractBase != CapabilityName::Invalid) + { + mask.add((UInt)abstractBase); + } + return mask; +} + +static UIntSet _calcConflictMask(const CapabilityConjunctionSet& set) { // Given a capbility set, we want to compute the mask representing // all groups of features for which it holds a potentially-conflicting atom. // - uint32_t mask = 0; - for( auto atom : m_expandedAtoms ) + UIntSet mask; + for (auto atom : set.getExpandedAtoms()) { - mask |= uint32_t(_getInfo(atom).conflictMask); + auto abstractBase = _getInfo(atom).abstractBase; + if (abstractBase != CapabilityName::Invalid) + { + mask.add((UInt)abstractBase); + } } return mask; } -bool CapabilitySet::isIncompatibleWith(CapabilitySet const& that) const +bool CapabilityConjunctionSet::isIncompatibleWith(CapabilityConjunctionSet const& that) const { // The `this` and `that` sets are incompatible if there exists // an atom A in `this` and an atom `B` in `that` such that - // A and B are not equal, but the two have overlapping "conflict mask." + // A and B are not equal, but the two have overlapping "conflict group." // // Equivalently, we can say that the two are in conflict if // @@ -382,8 +335,8 @@ bool CapabilitySet::isIncompatibleWith(CapabilitySet const& that) const // We start by identifying the OR of the conflict masks for // all features in `this` and `that`. // - uint32_t thisMask = this->_calcConflictMask(); - uint32_t thatMask = that._calcConflictMask(); + UIntSet thisMask = _calcConflictMask(*this); + UIntSet thatMask = _calcConflictMask(that); // Note: there is a possible early-exit opportunity here if // `thisMask` and `thatMask` have no overlap: there could @@ -422,8 +375,8 @@ bool CapabilitySet::isIncompatibleWith(CapabilitySet const& that) const // include something with an overlapping mask // (we don't know what at this point in the code). // - auto thisAtomMask = uint32_t(_getInfo(thisAtom).conflictMask); - if(thisAtomMask & thatMask) + auto thisConflictMask = Slang::_calcConflictMask(thisAtom); + if(UIntSet::hasIntersection(thisConflictMask, thatMask)) return true; thisIndex++; } @@ -435,8 +388,8 @@ bool CapabilitySet::isIncompatibleWith(CapabilitySet const& that) const // // The logic here is the mirror image of the case above. // - auto thatAtomMask = uint32_t(_getInfo(thatAtom).conflictMask); - if(thatAtomMask & thisMask) + auto thatConflictMask = Slang::_calcConflictMask(thatAtom); + if(UIntSet::hasIntersection(thatConflictMask, thisMask)) return true; thatIndex++; } @@ -445,7 +398,7 @@ bool CapabilitySet::isIncompatibleWith(CapabilitySet const& that) const return false; } -bool CapabilitySet::implies(CapabilitySet const& that) const +bool CapabilityConjunctionSet::implies(CapabilityConjunctionSet const& that) const { // One capability set implies another if it is a super-set // of the other one. Think of it this way: if your target @@ -541,37 +494,19 @@ struct CapabilityAtomComparator } }; -bool CapabilitySet::implies(CapabilityAtom atom) const +bool CapabilityConjunctionSet::implies(CapabilityAtom atom) const { - // The common case here is when `atom` is not an alias. + // Every non-alias atom that `this` implies should + // be presented in the `m_expandedAtoms` list. // - if( _getInfo(atom).flavor != CapabilityAtomFlavor::Alias ) - { - // Every non-alias atom that `this` implies should - // be presented in the `m_expandedAtoms` list. - // - // Because the list is sorted, we can find out whether - // it contains `atom` with a binary search. - // - Index result = m_expandedAtoms.binarySearch(atom, CapabilityAtomComparator()); - return result >= 0; - } - else - { - // In the case where `atom` is an alias, then it won't - // appear in the expanded list, and we need to check - // whether `this` set implies everything that `atom` - // transitively inherits from. - // - // The simplest way to do that is to expand `atom` - // into the full capability set it stands for and - // check that. - // - return implies(CapabilitySet(atom)); - } + // Because the list is sorted, we can find out whether + // it contains `atom` with a binary search. + // + Index result = m_expandedAtoms.binarySearch(atom, CapabilityAtomComparator()); + return result >= 0; } -Int CapabilitySet::countIntersectionWith(CapabilitySet const& that) const +Int CapabilityConjunctionSet::countIntersectionWith(CapabilityConjunctionSet const& that) const { // The goal of this subroutine is to count the number of // elements in the intersection of `this` and `that`, @@ -626,9 +561,9 @@ Int CapabilitySet::countIntersectionWith(CapabilitySet const& that) const return intersectionCount; } -bool CapabilitySet::isBetterForTarget( - CapabilitySet const& existingCaps, - CapabilitySet const& targetCaps) +bool CapabilityConjunctionSet::isBetterForTarget( + CapabilityConjunctionSet const& existingCaps, + CapabilityConjunctionSet const& targetCaps) const { auto& candidateCaps = *this; @@ -720,14 +655,12 @@ bool CapabilitySet::isBetterForTarget( // All preceding factors being equal, we prefer // a candidate that is strictly more specialized than the other. // - // TODO: This logic has the negative effect of always preferring - // to enable optional features even if they aren't necessary. - // It would prefer the set {glsl, optionalFeature} over the set - // {glsl}, even though we might argue that a default implementaton - // that works without any optional features is "obviously" what - // the user means if they didn't enable those features. + // We want to avoid choosing the candidate that uses + // optional features if they aren't necessary. + // For example, the set {glsl, optionalFeature} should not be preferred + // over the set {glsl}, if optionalFeature isn't requested explictly. // - // TODO: The right answer is possibly that we want to partition + // The solution here is that we want to partition // `candidateCaps` and `existingCaps` into two parts: their // intersection with `targetCaps` and their difference with it. // @@ -736,8 +669,28 @@ bool CapabilitySet::isBetterForTarget( // part we'd actually wnat to favor a definition that is less // specialized. // - if(candidateCaps.implies(existingCaps)) return true; - if(existingCaps.implies(candidateCaps)) return true; + CapabilityConjunctionSet candidateCapsIntersection; + CapabilityConjunctionSet candidateCapsDifference; + for (auto atom : candidateCaps.m_expandedAtoms) + { + if (targetCaps.implies(atom)) + candidateCapsIntersection.m_expandedAtoms.add(atom); + else + candidateCapsDifference.m_expandedAtoms.add(atom); + } + CapabilityConjunctionSet existingCapsIntersection; + CapabilityConjunctionSet existingCapsDifference; + for (auto atom : existingCaps.m_expandedAtoms) + { + if (targetCaps.implies(atom)) + existingCapsIntersection.m_expandedAtoms.add(atom); + else + existingCapsDifference.m_expandedAtoms.add(atom); + } + auto scoreCandidate = candidateCapsIntersection.m_expandedAtoms.getCount() - candidateCapsDifference.m_expandedAtoms.getCount(); + auto scoreExisting = existingCapsIntersection.m_expandedAtoms.getCount() - existingCapsDifference.m_expandedAtoms.getCount(); + if (scoreCandidate != scoreExisting) + return scoreCandidate > scoreExisting; // At this point we have the problem that neither candidate // appears to be "obviously" better for the target, but we @@ -747,18 +700,15 @@ bool CapabilitySet::isBetterForTarget( // different from the other, and see if anything in either case // has a ranking that should make it be preferred. // - // TODO: This should probably *not* be considering anything that - // is implied/supported by the target. - // - auto candidateScore = candidateCaps._calcDifferenceScoreWith(existingCaps); - auto existingScore = existingCaps._calcDifferenceScoreWith(candidateCaps); + auto candidateScore = candidateCapsDifference._calcDifferenceScoreWith(existingCapsDifference); + auto existingScore = existingCapsDifference._calcDifferenceScoreWith(candidateCapsDifference); if(candidateScore != existingScore) return candidateScore > existingScore; return false; } -uint32_t CapabilitySet::_calcDifferenceScoreWith(CapabilitySet const& that) const +uint32_t CapabilityConjunctionSet::_calcDifferenceScoreWith(CapabilityConjunctionSet const& that) const { uint32_t score = 0; @@ -811,12 +761,345 @@ uint32_t CapabilitySet::_calcDifferenceScoreWith(CapabilitySet const& that) cons } -bool CapabilitySet::operator==(CapabilitySet const& other) const +bool CapabilityConjunctionSet::operator==(CapabilityConjunctionSet const& other) const +{ + return m_expandedAtoms == other.m_expandedAtoms; +} + +bool CapabilityConjunctionSet::operator<(CapabilityConjunctionSet const& that) const +{ + for (Index i = 0; i < Math::Min(m_expandedAtoms.getCount(), that.m_expandedAtoms.getCount()); i++) + { + if (m_expandedAtoms[i] < that.m_expandedAtoms[i]) + return true; + else if (m_expandedAtoms[i] > that.m_expandedAtoms[i]) + return false; + } + return m_expandedAtoms.getCount() < that.m_expandedAtoms.getCount(); +} + + +CapabilitySet::CapabilitySet() +{} + +CapabilitySet::CapabilitySet(Int atomCount, CapabilityName const* atoms) +{ + for (Int i = 0; i < atomCount; i++) + addCapability(atoms[i]); +} + +CapabilitySet::CapabilitySet(CapabilityName atom) +{ + auto info = _getInfo(atom); + for (auto conjunction : info.canonicalRepresentation) + { + CapabilityConjunctionSet set; + for (auto atomName : conjunction) + set.getExpandedAtoms().add(asAtom(atomName)); + m_conjunctions.add(_Move(set)); + } +} + +CapabilitySet::CapabilitySet(List<CapabilityName> const& atoms) +{ + for (auto atom : atoms) + addCapability(atom); +} + +CapabilitySet CapabilitySet::makeEmpty() +{ + return CapabilitySet(); +} + +CapabilitySet CapabilitySet::makeInvalid() +{ + // An invalid capability set will always be a singleton + // set of the `Invalid` atom, and we will construct + // the set directly rather than use the more expensive + // logic in `_init()`. + // + CapabilitySet result; + result.m_conjunctions.add(CapabilityConjunctionSet(CapabilityAtom::Invalid)); + return result; +} + +void CapabilitySet::addCapability(CapabilityName name) +{ + join(CapabilitySet(name)); +} + +bool CapabilitySet::isEmpty() const +{ + return m_conjunctions.getCount() == 0; +} + +bool CapabilitySet::isInvalid() const +{ + return m_conjunctions.getCount() == 1 && m_conjunctions[0].isInvalid(); +} + +bool CapabilitySet::isIncompatibleWith(CapabilityAtom other) const +{ + if (isEmpty()) + return false; + + // If all conjunctions are incompatible with the atom, then we are incompatible. + for (auto& c : m_conjunctions) + if (!c.isIncompatibleWith(other)) + return false; + return true; +} + +bool CapabilitySet::isIncompatibleWith(CapabilityName other) const +{ + if (isEmpty()) + return false; + auto otherSet = CapabilitySet(other); + return isIncompatibleWith(otherSet); +} + +bool CapabilitySet::isIncompatibleWith(CapabilityConjunctionSet const& other) const { - // TODO: We should be able to implement this more efficiently - // by scanning over the two sets in tandem. + if (isEmpty()) + return false; + + // If all conjunctions are incompatible with the atom, then we are incompatible. + for (auto& c : m_conjunctions) + if (!c.isIncompatibleWith(other)) + return false; + return true; +} + +bool CapabilitySet::isIncompatibleWith(CapabilitySet const& other) const +{ + if (isEmpty()) + return false; + if (other.isEmpty()) + return false; + + // If all conjunctions in other are incompatible with the this set, then we are incompatible. + for (auto& oc : other.m_conjunctions) + for (auto& c : m_conjunctions) + if (!c.isIncompatibleWith(oc)) + return false; + return true; +} + +bool CapabilitySet::implies(CapabilityAtom atom) const +{ + if (isEmpty()) + return false; + + for (auto& c : m_conjunctions) + if (c.implies(atom)) + return true; + + return false; +} + +bool CapabilitySet::implies(const CapabilityConjunctionSet& set) const +{ + if (isEmpty()) + return false; + + for (auto& c : m_conjunctions) + if (c.implies(set)) + return true; - return this->implies(other) && other.implies(*this); + return false; +} + +bool CapabilitySet::implies(CapabilitySet const& other) const +{ + // x implies (c | d) only if (x implies c) and (x implies d). + if (other.isEmpty()) + return true; + for (auto& c : other.m_conjunctions) + if (!implies(c)) + return false; + return true; +} + +bool CapabilitySet::operator==(CapabilitySet const& that) const +{ + return m_conjunctions == that.m_conjunctions; +} + +void CapabilitySet::calcCompactedAtoms(List<List<CapabilityAtom>>& outAtoms) const +{ + for (auto& c : m_conjunctions) + { + List<CapabilityAtom> atoms; + c.calcCompactedAtoms(atoms); + outAtoms.add(atoms); + } +} + +void CapabilitySet::join(const CapabilitySet& other) +{ + if (isEmpty() || other.isInvalid()) + { + *this = other; + return; + } + if (isInvalid()) + return; + if (other.isEmpty()) + return; + + List<CapabilityConjunctionSet> resultSet; + for (auto& thatConjunction : other.m_conjunctions) + { + for (auto& thisConjunction : m_conjunctions) + { + if (thisConjunction.isIncompatibleWith(thatConjunction)) + continue; + + CapabilityConjunctionSet conjunction; + CapabilityConjunctionSet *conjunctionToAdd = nullptr; + + // Add atoms from thatConjunction that are not existant in thisConjunction. + for (auto atom : thatConjunction.getExpandedAtoms()) + { + if (thisConjunction.getExpandedAtoms().binarySearch(atom, CapabilityAtomComparator()) == -1) + { + conjunction.getExpandedAtoms().add(atom); + } + } + + if (conjunction.getExpandedAtoms().getCount() != 0) + { + // If we find any capabilities in thatConjunction that is missing from thisConjunction, + // create a new ConjunctionSet that contains atoms from both, and add it to the disjunction set. + conjunction.getExpandedAtoms().addRange(thisConjunction.getExpandedAtoms()); + conjunction.getExpandedAtoms().sort(); + conjunctionToAdd = &conjunction; + } + else + { + // Otherwise, thisConjunction implies thatConjunction, so we just add thisConjunction to resultSet. + conjunctionToAdd = &thisConjunction; + } + // We add conjunctionToAdd to resultSet only if it does not imply any existing conjunctions. + // For example, if `resultSet` is (a), and conjunctionToAdd is (ab), then we don't want to add the conjunction + // to form (a | ab) because that would reduce to (a). + bool skipAdd = false; + for (auto& c : resultSet) + { + if (conjunctionToAdd->implies(c)) + { + skipAdd = true; + break; + } + } + if (!skipAdd) + { + // Once we added the new conjunction, any existing conjunctions that implies the new one can be + // removed. + // For example, if resultSet was (ab), and we are adding (a), the result should be just (a). + for (Index i = 0; i < resultSet.getCount();) + { + if (resultSet[i].implies(*conjunctionToAdd)) + { + resultSet.fastRemoveAt(i); + } + else + { + i++; + } + } + resultSet.add(*conjunctionToAdd); + } + } + } + m_conjunctions = _Move(resultSet); + + // Make sure conjunctions are sorted so equality tests are trivial. + m_conjunctions.sort(); +} + +bool CapabilitySet::isBetterForTarget(CapabilitySet const& that, CapabilitySet const& targetCaps) const +{ + if (targetCaps.isIncompatibleWith(*this)) + return false; + if (targetCaps.isIncompatibleWith(that)) + return true; + + ArrayView<CapabilityConjunctionSet> thisSets = m_conjunctions.getArrayView(); + ArrayView<CapabilityConjunctionSet> thatSets = that.m_conjunctions.getArrayView(); + CapabilityConjunctionSet emtpySet = CapabilityConjunctionSet::makeEmpty(); + + if (isEmpty()) + thisSets = makeArrayViewSingle(emtpySet); + if (that.isEmpty()) + thatSets = makeArrayViewSingle(emtpySet); + + // It is hard to think about what it means exactly to compare a general disjunction set to another with regard + // to a target that itself is also a disjunction set. + // Instead of trying to find a meaning for the general case, we just want to extend the logic + // for conjunction sets to disjunction sets in a way that common situations are handled correctly. + // Note that when we reach here, most of these sets are likely to contain only one conjunction, so + // we just need to make sure the more general logic here yields correct result for that case. + // + // Right now, we define betterness for disjunctions as follows: + // A capability set X is determined to be better for a target T than capability set Y, + // if we find a conjunction A in X and a conjunction B in Y and a conjunction C in T such that + // A is better then B for target C. + // + struct ViableConjunctionIndex + { + Index index; + UIntSet targetConjunctionIndices; + }; + auto getViableConjunction = [&](ArrayView<CapabilityConjunctionSet> set, List<ViableConjunctionIndex>& outList) + { + for (Index i = 0; i < set.getCount(); i++) + { + auto& conjunction = set[i]; + ViableConjunctionIndex viableConjunction; + viableConjunction.index = i; + for (Index j = 0; j < targetCaps.m_conjunctions.getCount(); j++) + { + auto& targetConjunction = targetCaps.m_conjunctions[j]; + if (conjunction.isIncompatibleWith(targetConjunction)) + continue; + viableConjunction.targetConjunctionIndices.add(j); + } + if (!viableConjunction.targetConjunctionIndices.isEmpty()) + { + outList.add(viableConjunction); + } + } + }; + List<ViableConjunctionIndex> viableConjunctionsThis; + List<ViableConjunctionIndex> viableConjunctionsThat; + + getViableConjunction(thisSets, viableConjunctionsThis); + getViableConjunction(thatSets, viableConjunctionsThat); + + for (auto& thisConjunctionIndex : viableConjunctionsThis) + { + auto& thisConjunction = thisSets[thisConjunctionIndex.index]; + for (auto& thatConjunctionIndex : viableConjunctionsThat) + { + auto& thatConjunction = thatSets[thatConjunctionIndex.index]; + UIntSet intersection = thisConjunctionIndex.targetConjunctionIndices; + intersection.intersectWith(thatConjunctionIndex.targetConjunctionIndices); + if (!intersection.isEmpty()) + { + for (Index targetConjunctionIndex = 0; targetConjunctionIndex < targetCaps.m_conjunctions.getCount(); targetConjunctionIndex++) + { + if (!intersection.contains((UInt)targetConjunctionIndex)) + continue; + if (thisConjunction.isBetterForTarget(thatConjunction, targetCaps.m_conjunctions[targetConjunctionIndex])) + { + return true; + } + } + } + } + } + return false; } } diff --git a/source/slang/slang-capability.h b/source/slang/slang-capability.h index 55c5044ed..e686ff5a9 100644 --- a/source/slang/slang-capability.h +++ b/source/slang/slang-capability.h @@ -25,22 +25,7 @@ namespace Slang // the `CapabilityAtom` enumeration, which is generated from declarations // in the `slang-capability-defs.h` file. // -enum class CapabilityAtom : int32_t -{ - // The "invalid" capability represents an atomic feature that no - // platform can/will ever support. If we ever determine that a - // function needs the invalid capability, it would be reasonable - // to report that situation as an error. - // - Invalid = 0, - -#define SLANG_CAPABILITY_ATOM(ENUMERATOR, NAME, FLAVOR, CONFLICT, RANK, BASE0, BASE1, BASE2, BASE3) \ - ENUMERATOR, - -#include "slang-capability-defs.h" - - Count, -}; +#include "slang-generated-capability-defs.h" // Once we have a universe of suitable capability atoms, we can define // the capabilities of a target as simply the set of all atomic capabilities @@ -62,26 +47,31 @@ enum class CapabilityAtom : int32_t // In all cases, we represent a set of capabilities with `CapabilitySet`. /// A set of capabilities, representing features that are either supported or required -struct CapabilitySet +struct CapabilityConjunctionSet { public: /// Default-construct an empty capability set - CapabilitySet(); + CapabilityConjunctionSet(); + + CapabilityConjunctionSet(CapabilityConjunctionSet const& other) = default; + CapabilityConjunctionSet& operator=(CapabilityConjunctionSet const& other) = default; + CapabilityConjunctionSet(CapabilityConjunctionSet&& other) = default; + CapabilityConjunctionSet& operator=(CapabilityConjunctionSet&& other) = default; /// Construct a capability set from an explicit list of atomic capabilities - CapabilitySet(Int atomCount, CapabilityAtom const* atoms); + CapabilityConjunctionSet(Int atomCount, CapabilityAtom const* atoms); /// Construct a capability set from an explicit list of atomic capabilities - explicit CapabilitySet(List<CapabilityAtom> const& atoms); + explicit CapabilityConjunctionSet(List<CapabilityAtom> const& atoms); /// Construct a singleton set from a single atomic capability - explicit CapabilitySet(CapabilityAtom atom); + explicit CapabilityConjunctionSet(CapabilityAtom atom); /// Make an empty capability set - static CapabilitySet makeEmpty(); + static CapabilityConjunctionSet makeEmpty(); /// Make an invalid capability set (such that no target could ever support it) - static CapabilitySet makeInvalid(); + static CapabilityConjunctionSet makeInvalid(); /// Is this capability set empty (such that any target supports it)? bool isEmpty() const; @@ -105,7 +95,7 @@ public: bool isIncompatibleWith(CapabilityAtom other) const; /// Is this capability set incompatible with the given `other` atomic capability. - bool isIncompatibleWith(CapabilitySet const& other) const; + bool isIncompatibleWith(CapabilityConjunctionSet const& other) const; // One capability set A "implies" another set B if a target that // supports A must also support all of B. @@ -116,7 +106,7 @@ public: // and not get hung up on the set theory. /// Does this capability set imply all the capabilities in `other`? - bool implies(CapabilitySet const& other) const; + bool implies(CapabilityConjunctionSet const& other) const; /// Does this capability set imply the atomic capability `other`? bool implies(CapabilityAtom other) const; @@ -124,23 +114,24 @@ public: // A capability set is equal to another if each implies the other. /// Are these two capability sets equal? - bool operator==(CapabilitySet const& that) const; + bool operator==(CapabilityConjunctionSet const& that) const; + bool operator<(CapabilityConjunctionSet const& that) const; /// Get access to the raw atomic capabilities that define this set. List<CapabilityAtom> const& getExpandedAtoms() const { return m_expandedAtoms; } + List<CapabilityAtom>& getExpandedAtoms() { return m_expandedAtoms; } /// Calculate a list of "compacted" atoms, which excludes any atoms from the expanded list that are implies by another item in the list. void calcCompactedAtoms(List<CapabilityAtom>& outAtoms) const; - Int countIntersectionWith(CapabilitySet const& that) const; + Int countIntersectionWith(CapabilityConjunctionSet const& that) const; - bool isBetterForTarget(CapabilitySet const& that, CapabilitySet const& targetCaps); + bool isBetterForTarget(CapabilityConjunctionSet const& that, CapabilityConjunctionSet const& targetCaps) const; private: void _init(Int atomCount, CapabilityAtom const* atoms); - uint32_t _calcConflictMask() const; - uint32_t _calcDifferenceScoreWith(CapabilitySet const& other) const; + uint32_t _calcDifferenceScoreWith(CapabilityConjunctionSet const& other) const; // The underlying representation we use is a sorted and deduplicated // list of all the (non-alias) atoms that are present in the set. @@ -151,6 +142,92 @@ private: }; /// Are the `left` and `right` capability sets unequal? +inline bool operator!=(CapabilityConjunctionSet const& left, CapabilityConjunctionSet const& right) +{ + return !(left == right); +} + +struct CapabilitySet +{ +public: + /// Default-construct an empty capability set + CapabilitySet(); + + CapabilitySet(CapabilitySet const& other) = default; + CapabilitySet& operator=(CapabilitySet const& other) = default; + CapabilitySet(CapabilitySet&& other) = default; + CapabilitySet& operator=(CapabilitySet&& other) = default; + + /// Construct a capability set from an explicit list of atomic capabilities + CapabilitySet(Int atomCount, CapabilityName const* atoms); + + /// Construct a capability set from an explicit list of atomic capabilities + explicit CapabilitySet(List<CapabilityName> const& atoms); + + /// Construct a singleton set from a single atomic capability + explicit CapabilitySet(CapabilityName atom); + + /// Construct a singleton set from conjunctions + explicit CapabilitySet(const List<CapabilityConjunctionSet>& conjunctions); + + /// Make an empty capability set + static CapabilitySet makeEmpty(); + + /// Make an invalid capability set (such that no target could ever support it) + static CapabilitySet makeInvalid(); + + /// Is this capability set empty (such that any target supports it)? + bool isEmpty() const; + + /// Is this capability set invalid (such that no target could support it)? + bool isInvalid() const; + + /// Is this capability set incompatible with the given `other` set. + bool isIncompatibleWith(CapabilityAtom other) const; + + /// Is this capability set incompatible with the given `other` set. + bool isIncompatibleWith(CapabilityName other) const; + + /// Is this capability set incompatible with the given `other` atomic capability. + bool isIncompatibleWith(CapabilityConjunctionSet const& other) const; + + /// Is this capability set incompatible with the given `other` atomic capability. + bool isIncompatibleWith(CapabilitySet const& other) const; + + /// Does this capability set imply all the capabilities in `other`? + bool implies(CapabilitySet const& other) const; + + /// Does this capability set imply all the capabilities in `other`? + bool implies(CapabilityConjunctionSet const& other) const; + + /// Does this capability set imply the atomic capability `other`? + bool implies(CapabilityAtom other) const; + + /// Join two capability sets to form (this & other). + void join(const CapabilitySet& other); + + /// Are these two capability sets equal? + bool operator==(CapabilitySet const& that) const; + + /// Get access to the raw atomic capabilities that define this set. + List<CapabilityConjunctionSet>& getExpandedAtoms() { return m_conjunctions; } + const List<CapabilityConjunctionSet>& getExpandedAtoms() const { return m_conjunctions; } + + + /// Calculate a list of "compacted" atoms, which excludes any atoms from the expanded list that are implies by another item in the list. + void calcCompactedAtoms(List<List<CapabilityAtom>>& outAtoms) const; + + bool isBetterForTarget(CapabilitySet const& that, CapabilitySet const& targetCaps) const; + +private: + // The underlying representation we use is a list of conjunctions. + // + List<CapabilityConjunctionSet> m_conjunctions; + + void addCapability(CapabilityName name); +}; + +/// Are the `left` and `right` capability sets unequal? inline bool operator!=(CapabilitySet const& left, CapabilitySet const& right) { return !(left == right); @@ -160,9 +237,9 @@ inline bool operator!=(CapabilitySet const& left, CapabilitySet const& right) bool isCapabilityDerivedFrom(CapabilityAtom atom, CapabilityAtom base); /// Find a capability atom with the given `name`, or return CapabilityAtom::Invalid. -CapabilityAtom findCapabilityAtom(UnownedStringSlice const& name); +CapabilityName findCapabilityName(UnownedStringSlice const& name); /// Gets the capability names. -void getCapabilityAtomNames(List<UnownedStringSlice>& ioNames); +void getCapabilityNames(List<UnownedStringSlice>& ioNames); } diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index c2bb515f6..491655643 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -584,19 +584,23 @@ namespace Slang GLSLExtensionTracker* extensionTracker, CapabilitySet const& caps) { - for( auto atom : caps.getExpandedAtoms() ) + for( auto conjunctions : caps.getExpandedAtoms() ) { - switch( atom ) + for (auto atom : conjunctions.getExpandedAtoms()) { - default: - break; - - case CapabilityAtom::SPIRV_1_0: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 0)); break; - case CapabilityAtom::SPIRV_1_1: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 1)); break; - case CapabilityAtom::SPIRV_1_2: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 2)); break; - case CapabilityAtom::SPIRV_1_3: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 3)); break; - case CapabilityAtom::SPIRV_1_4: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 4)); break; - case CapabilityAtom::SPIRV_1_5: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 5)); break; + switch (atom) + { + default: + break; + + case CapabilityAtom::glsl_spirv_1_0: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 0)); break; + case CapabilityAtom::glsl_spirv_1_1: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 1)); break; + case CapabilityAtom::glsl_spirv_1_2: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 2)); break; + case CapabilityAtom::glsl_spirv_1_3: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 3)); break; + case CapabilityAtom::glsl_spirv_1_4: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 4)); break; + case CapabilityAtom::glsl_spirv_1_5: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 5)); break; + case CapabilityAtom::glsl_spirv_1_6: extensionTracker->requireSPIRVVersion(SemanticVersion(1, 6)); break; + } } } } diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index 8a56df3af..a95b5a6bc 100755 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -1620,7 +1620,7 @@ namespace Slang forceGLSLScalarBufferLayout = value; } - void addCapability(CapabilityAtom capability); + void addCapability(CapabilityName capability); bool shouldEmitSPIRVDirectly() { @@ -1666,7 +1666,7 @@ namespace Slang SlangTargetFlags targetFlags = kDefaultTargetFlags; Slang::Profile targetProfile = Slang::Profile(); FloatingPointMode floatingPointMode = FloatingPointMode::Default; - List<CapabilityAtom> rawCapabilities; + List<CapabilityName> rawCapabilities; CapabilitySet cookedCapabilities; LineDirectiveMode lineDirectiveMode = LineDirectiveMode::Default; bool dumpIntermediates = false; diff --git a/source/slang/slang-doc-markdown-writer.cpp b/source/slang/slang-doc-markdown-writer.cpp index de7695d18..77b239437 100644 --- a/source/slang/slang-doc-markdown-writer.cpp +++ b/source/slang/slang-doc-markdown-writer.cpp @@ -432,20 +432,21 @@ static DocMarkdownWriter::Requirement _getRequirementFromTargetToken(const Token return Requirement{CodeGenTarget::SPIRV, UnownedStringSlice("")}; } - const CapabilityAtom targetCap = findCapabilityAtom(targetName); + const CapabilityAtom targetCap = (CapabilityAtom)findCapabilityName(targetName); if (targetCap == CapabilityAtom::Invalid) { return Requirement{ CodeGenTarget::None, String() }; } + SLANG_ASSERT(targetCap < CapabilityAtom::Count); static const CapabilityAtom rootAtoms[] = { - CapabilityAtom::GLSL, - CapabilityAtom::HLSL, - CapabilityAtom::CUDA, - CapabilityAtom::CPP, - CapabilityAtom::C, + CapabilityAtom::glsl, + CapabilityAtom::hlsl, + CapabilityAtom::cuda, + CapabilityAtom::cpp, + CapabilityAtom::c, }; for (auto rootAtom : rootAtoms) @@ -458,23 +459,23 @@ static DocMarkdownWriter::Requirement _getRequirementFromTargetToken(const Token } } - if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::GLSL)) + if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::glsl)) { return Requirement{CodeGenTarget::GLSL, targetName}; } - else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::HLSL)) + else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::hlsl)) { return Requirement{ CodeGenTarget::HLSL, targetName }; } - else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::CUDA)) + else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::cuda)) { return Requirement{ CodeGenTarget::CUDASource, targetName }; } - else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::CPP)) + else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::cpp)) { return Requirement{ CodeGenTarget::CPPSource, targetName }; } - else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::C)) + else if (isCapabilityDerivedFrom(targetCap, CapabilityAtom::c)) { return Requirement{ CodeGenTarget::CSource, targetName }; } diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index ba5606791..8aa550a03 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -1214,7 +1214,8 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst) case kIROp_IntLit: case kIROp_FloatLit: case kIROp_BoolLit: - case kIROp_CapabilitySet: + case kIROp_CapabilityConjunction: + case kIROp_CapabilityDisjunction: return true; // Always fold these in, because their results diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp index 0fc54ca1f..bff82acf3 100644 --- a/source/slang/slang-emit-glsl.cpp +++ b/source/slang/slang-emit-glsl.cpp @@ -70,7 +70,7 @@ void GLSLSourceEmitter::_requireRayTracing() // the user has explicitly opted in to the `GL_NV_ray_tracing` extension // we will use that one instead. // - if( getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing) ) + if( getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing) ) { m_glslExtensionTracker->requireExtension(UnownedStringSlice::fromLiteral("GL_NV_ray_tracing")); } @@ -92,7 +92,7 @@ void GLSLSourceEmitter::_requireFragmentShaderBarycentric() // the user has explicitly opted in to the `GL_NV_fragment_shader_barycentric` extension // we will use that one instead. - if( getTargetCaps().implies(CapabilityAtom::GL_NV_fragment_shader_barycentric) ) + if( getTargetCaps().implies(CapabilityAtom::_GL_NV_fragment_shader_barycentric) ) { m_glslExtensionTracker->requireExtension(UnownedStringSlice::fromLiteral("GL_NV_fragment_shader_barycentric")); } @@ -657,7 +657,7 @@ bool GLSLSourceEmitter::_emitGLSLLayoutQualifierWithBindingKinds(LayoutResourceK m_writer->emit("layout(push_constant)\n"); break; case LayoutResourceKind::ShaderRecord: - if (getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing)) + if (getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing)) { m_writer->emit("layout(shaderRecordNV)\n"); } @@ -1398,7 +1398,7 @@ void GLSLSourceEmitter::emitLayoutQualifiersImpl(IRVarLayout* layout) case LayoutResourceKind::RayPayload: { - if( getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing) ) + if( getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing) ) { m_writer->emit("rayPayloadInNV "); } @@ -1411,7 +1411,7 @@ void GLSLSourceEmitter::emitLayoutQualifiersImpl(IRVarLayout* layout) case LayoutResourceKind::CallablePayload: { - if( getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing) ) + if( getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing) ) { m_writer->emit("callableDataInNV "); } @@ -1424,7 +1424,7 @@ void GLSLSourceEmitter::emitLayoutQualifiersImpl(IRVarLayout* layout) case LayoutResourceKind::HitAttributes: { - if( getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing) ) + if( getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing) ) { m_writer->emit("hitAttributeNV "); } @@ -2421,7 +2421,7 @@ void GLSLSourceEmitter::emitSimpleTypeImpl(IRType* type) // `GL_NV_ray_tracing` using target capabilities, and will always default // to `GL_EXT_ray_tracing` otherwise. // - if( getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing) ) + if( getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing) ) { // If the user has explicitly opted in to `GL_NV_ray_tracing`, // then we don't need to explicitly request the extentsion again. @@ -2509,7 +2509,7 @@ bool GLSLSourceEmitter::_maybeEmitInterpolationModifierText(IRInterpolationMode if( stage == Stage::Fragment && isInput) { _requireFragmentShaderBarycentric(); - if (getTargetCaps().implies(CapabilityAtom::GL_NV_fragment_shader_barycentric)) + if (getTargetCaps().implies(CapabilityAtom::_GL_NV_fragment_shader_barycentric)) { m_writer->emit("pervertexNV "); } @@ -2657,7 +2657,7 @@ void GLSLSourceEmitter::emitVarDecorationsImpl(IRInst* varDecl) // Special case hitObjectAttribute as is only NV currently if (decoration->getOp() == kIROp_VulkanHitObjectAttributesDecoration || - getTargetCaps().implies(CapabilityAtom::GL_NV_ray_tracing)) + getTargetCaps().implies(CapabilityAtom::_GL_NV_ray_tracing)) { m_writer->emit(toSlice("NV")); } diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp index aa277cfba..07f8a4cd2 100644 --- a/source/slang/slang-emit-spirv.cpp +++ b/source/slang/slang-emit-spirv.cpp @@ -3161,7 +3161,7 @@ struct SPIRVEmitContext } else if (semanticName == "sv_barycentrics") { - if (m_targetRequest->getTargetCaps().implies(CapabilityAtom::GL_NV_fragment_shader_barycentric)) + if (m_targetRequest->getTargetCaps().implies(CapabilityAtom::_GL_NV_fragment_shader_barycentric)) { requireSPIRVCapability(SpvCapabilityFragmentBarycentricNV); ensureExtensionDeclaration(UnownedStringSlice("SPV_NV_fragment_shader_barycentric")); diff --git a/source/slang/slang-ir-dll-import.cpp b/source/slang/slang-ir-dll-import.cpp index 2da89ad3e..fbb473ea2 100644 --- a/source/slang/slang-ir-dll-import.cpp +++ b/source/slang/slang-ir-dll-import.cpp @@ -28,7 +28,7 @@ struct DllImportContext auto funcType = builder.getFuncType(paramCount, paramTypes, resultType); builder.setDataType(result, funcType); builder.addTargetIntrinsicDecoration( - result, CapabilitySet(CapabilityAtom::CPP), targetIntrinsic); + result, CapabilitySet(CapabilityName::cpp), targetIntrinsic); return result; } diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp index 7bd4f77a3..4d2b12bc4 100644 --- a/source/slang/slang-ir-glsl-legalize.cpp +++ b/source/slang/slang-ir-glsl-legalize.cpp @@ -914,7 +914,7 @@ GLSLSystemValueInfo* getGLSLSystemValueInfo( else if (semanticName == "sv_barycentrics") { context->requireGLSLVersion(ProfileVersion::GLSL_450); - if (codeGenContext->getTargetCaps().implies(CapabilityAtom::GL_NV_fragment_shader_barycentric)) + if (codeGenContext->getTargetCaps().implies(CapabilityAtom::_GL_NV_fragment_shader_barycentric)) { context->requireGLSLExtension(UnownedStringSlice::fromLiteral("GL_NV_fragment_shader_barycentric")); name = "gl_BaryCoordNV"; @@ -2281,7 +2281,7 @@ static void legalizeMeshOutputParam( auto arrayName = isPerPrimitive ? "gl_MeshPrimitivesEXT" : "gl_MeshVerticesEXT"; builder->addTargetIntrinsicDecoration( meshOutputBlockType, - CapabilitySet(CapabilityAtom::GLSL), + CapabilitySet(CapabilityName::glsl), UnownedStringSlice(typeName)); builder->addImportDecoration(blockParam, UnownedStringSlice(arrayName)); if(isPerPrimitive) diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 99f73b025..453fbf16d 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -282,7 +282,9 @@ INST(Block, block, 0, PARENT) INST(VoidLit, void_constant, 0, 0) INST_RANGE(Constant, BoolLit, VoidLit) -INST(CapabilitySet, capabilitySet, 0, HOISTABLE) +INST(CapabilityConjunction, capabilityConjunction, 0, HOISTABLE) +INST(CapabilityDisjunction, capabilityDisjunction, 0, HOISTABLE) +INST_RANGE(CapabilitySet, CapabilityConjunction, CapabilityDisjunction) INST(undefined, undefined, 0, 0) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 4953a5287..2a7852c1e 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -20,7 +20,7 @@ class Decl; struct IRCapabilitySet : IRInst { - IR_LEAF_ISA(CapabilitySet); + IR_PARENT_ISA(CapabilitySet); CapabilitySet getCaps(); }; @@ -4688,7 +4688,7 @@ IRTargetSpecificDecoration* findBestTargetDecoration( IRTargetSpecificDecoration* findBestTargetDecoration( IRInst* val, - CapabilityAtom targetCapabilityAtom); + CapabilityName targetCapabilityAtom); bool findTargetIntrinsicDefinition(IRInst* callee, CapabilitySet const& targetCaps, UnownedStringSlice& outDefinition); diff --git a/source/slang/slang-ir-specialize-target-switch.cpp b/source/slang/slang-ir-specialize-target-switch.cpp index fcd5ca10a..f4cb6bfa7 100644 --- a/source/slang/slang-ir-specialize-target-switch.cpp +++ b/source/slang/slang-ir-specialize-target-switch.cpp @@ -18,11 +18,11 @@ namespace Slang IRBlock* targetBlock = nullptr; for (UInt i = 0; i < targetSwitch->getCaseCount(); i++) { - auto cap = (CapabilityAtom)getIntVal(targetSwitch->getCaseValue(i)); + auto cap = (CapabilityName)getIntVal(targetSwitch->getCaseValue(i)); if (target->getTargetCaps().isIncompatibleWith(cap)) continue; CapabilitySet capSet; - if (cap == CapabilityAtom::Invalid) + if (cap == CapabilityName::Invalid) capSet = CapabilitySet::makeEmpty(); else capSet = CapabilitySet(cap); diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index f9a1276fe..1dbb21a0d 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -287,16 +287,36 @@ namespace Slang CapabilitySet IRCapabilitySet::getCaps() { - List<CapabilityAtom> atoms; - - Index count = (Index) getOperandCount(); - for(Index i = 0; i < count; ++i) + switch (getOp()) { - auto operand = cast<IRIntLit>(getOperand(i)); - atoms.add(CapabilityAtom(operand->getValue())); - } + case kIROp_CapabilityConjunction: + { + List<CapabilityName> atoms; + + Index count = (Index)getOperandCount(); + for (Index i = 0; i < count; ++i) + { + auto operand = cast<IRIntLit>(getOperand(i)); + atoms.add(CapabilityName(operand->getValue())); + } - return CapabilitySet(atoms.getCount(), atoms.getBuffer()); + return CapabilitySet(atoms.getCount(), atoms.getBuffer()); + } + break; + case kIROp_CapabilityDisjunction: + { + CapabilitySet result; + Index count = (Index) getOperandCount(); + for (Index i = 0; i < count; ++i) + { + auto operand = cast<IRCapabilitySet>(getOperand(i)); + result.getExpandedAtoms().addRange(operand->getCaps().getExpandedAtoms()); + } + return result; + } + break; + } + return CapabilitySet(); } @@ -2396,17 +2416,21 @@ namespace Slang // be a minimal list of atoms such that they will produce // the same `CapabilitySet` when expanded. - List<CapabilityAtom> compactedAtoms; + List<List<CapabilityAtom>> compactedAtoms; caps.calcCompactedAtoms(compactedAtoms); - - List<IRInst*> args; - for( auto atom : compactedAtoms ) + List<IRInst*> conjunctions; + for( auto atomConjunction : compactedAtoms ) { - args.add(getIntValue(capabilityAtomType, Int(atom))); + List<IRInst*> args; + for (auto atom : atomConjunction) + args.add(getIntValue(capabilityAtomType, Int(atom))); + auto conjunctionInst = createIntrinsicInst( + capabilitySetType, kIROp_CapabilityConjunction, args.getCount(), args.getBuffer()); + conjunctions.add(conjunctionInst); } - - return createIntrinsicInst( - capabilitySetType, kIROp_CapabilitySet, args.getCount(), args.getBuffer()); + if (conjunctions.getCount() == 1) + return conjunctions[0]; + return createIntrinsicInst(capabilitySetType, kIROp_CapabilityDisjunction, conjunctions.getCount(), conjunctions.getBuffer()); } static void canonicalizeInstOperands(IRBuilder& builder, IROp op, ArrayView<IRInst*> operands) @@ -8004,7 +8028,7 @@ namespace Slang IRTargetSpecificDecoration* findBestTargetDecoration( IRInst* val, - CapabilityAtom targetCapabilityAtom) + CapabilityName targetCapabilityAtom) { return findBestTargetDecoration(val, CapabilitySet(targetCapabilityAtom)); } diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 4ee4efc16..f624b0baa 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -8724,7 +8724,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // target, and we need to reflect that at the IR level. auto targetName = targetMod->targetToken.getContent(); - auto targetCap = findCapabilityAtom(targetName); + auto targetCap = findCapabilityName(targetName); getBuilder()->addTargetDecoration(inst, CapabilitySet(targetCap)); } @@ -8777,8 +8777,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> } else { - CapabilityAtom targetCap = findCapabilityAtom(targetName); - SLANG_ASSERT(targetCap != CapabilityAtom::Invalid); + CapabilityName targetCap = findCapabilityName(targetName); + SLANG_ASSERT(targetCap != CapabilityName::Invalid); targetCaps = CapabilitySet(targetCap); } @@ -8923,7 +8923,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> getBuilder()->addTargetIntrinsicDecoration( irInst, - CapabilitySet(CapabilityAtom::TEXTUAL_SOURCE), + CapabilitySet(CapabilityName::textualTarget), definition.getUnownedSlice() ); } diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp index 9830fb18c..7c442a9d2 100644 --- a/source/slang/slang-options.cpp +++ b/source/slang/slang-options.cpp @@ -307,7 +307,7 @@ void initCommandOptions(CommandOptions& options) "code accordingly."); List<UnownedStringSlice> names; - getCapabilityAtomNames(names); + getCapabilityNames(names); // We'll just add to keep the list more simple... options.addValue("spirv_1_{ 0,1,2,3,4,5 }", "minimum supported SPIR - V version"); @@ -315,11 +315,12 @@ void initCommandOptions(CommandOptions& options) for (auto name : names) { if (name.startsWith("__") || - name.startsWith("spirv_1_")) + name.startsWith("spirv_1_") || + name.startsWith("_")) { continue; } - else if (name.startsWith("GL_")) + else if (name.startsWith("GL_") || name.startsWith("SPV_") || name.startsWith("GLSL_")) { // We'll assume it is an extension.. StringBuilder buf; @@ -764,7 +765,7 @@ struct OptionsParser int targetID = -1; FloatingPointMode floatingPointMode = FloatingPointMode::Default; bool forceGLSLScalarLayout = false; - List<CapabilityAtom> capabilityAtoms; + List<CapabilityName> capabilityAtoms; // State for tracking command-line errors bool conflictingProfilesSet = false; @@ -796,7 +797,7 @@ struct OptionsParser RawTarget* getCurrentTarget(); void setProfileVersion(RawTarget* rawTarget, ProfileVersion profileVersion); - void addCapabilityAtom(RawTarget* rawTarget, CapabilityAtom atom); + void addCapabilityAtom(RawTarget* rawTarget, CapabilityName atom); void setFloatingPointMode(RawTarget* rawTarget, FloatingPointMode mode); @@ -1161,7 +1162,7 @@ void OptionsParser::setProfileVersion(RawTarget* rawTarget, ProfileVersion profi rawTarget->profileVersion = profileVersion; } -void OptionsParser::addCapabilityAtom(RawTarget* rawTarget, CapabilityAtom atom) +void OptionsParser::addCapabilityAtom(RawTarget* rawTarget, CapabilityName atom) { rawTarget->capabilityAtoms.add(atom); } @@ -1755,8 +1756,8 @@ SlangResult OptionsParser::_parseProfile(const CommandLineArg& arg) for (Index i = 1; i < sliceCount; ++i) { UnownedStringSlice atomName = slices[i]; - CapabilityAtom atom = findCapabilityAtom(atomName); - if (atom == CapabilityAtom::Invalid) + CapabilityName atom = findCapabilityName(atomName); + if (atom == CapabilityName::Invalid) { m_sink->diagnose(operand.loc, Diagnostics::unknownProfile, atomName); return SLANG_FAIL; @@ -2119,8 +2120,8 @@ SlangResult OptionsParser::_parse( for (Index i = 0; i < sliceCount; ++i) { UnownedStringSlice atomName = slices[i]; - CapabilityAtom atom = findCapabilityAtom(atomName); - if (atom == CapabilityAtom::Invalid) + CapabilityName atom = findCapabilityName(atomName); + if (atom == CapabilityName::Invalid) { m_sink->diagnose(operand.loc, Diagnostics::unknownProfile, atomName); return SLANG_FAIL; diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 62ab71430..909a8eb72 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -4868,8 +4868,8 @@ namespace Slang for (auto caseName : caseNames) { TargetCaseStmt* targetCase = parser->astBuilder->create<TargetCaseStmt>(); - auto cap = findCapabilityAtom(caseName.getContent()); - if (caseName.getContent().getLength() && cap == CapabilityAtom::Invalid) + auto cap = findCapabilityName(caseName.getContent()); + if (caseName.getContent().getLength() && cap == CapabilityName::Invalid) { parser->sink->diagnose(caseName.loc, Diagnostics::unknownTargetName, caseName.getContent()); } diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 7bc73fd2c..8eb75c119 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -661,7 +661,7 @@ SLANG_NO_THROW SlangProfileID SLANG_MCALL Session::findProfile( SLANG_NO_THROW SlangCapabilityID SLANG_MCALL Session::findCapability( char const* name) { - return SlangCapabilityID(Slang::findCapabilityAtom(UnownedTerminatedStringSlice(name))); + return SlangCapabilityID(Slang::findCapabilityName(UnownedTerminatedStringSlice(name))); } SLANG_NO_THROW void SLANG_MCALL Session::setDownstreamCompilerPath( @@ -1439,9 +1439,12 @@ void Linkage::buildHash(DigestBuilder<SHA1>& builder, SlangInt targetIndex) builder.append(targetReq->shouldTrackLiveness()); auto cookedCapabilities = targetReq->getTargetCaps().getExpandedAtoms(); - for (auto& capability : cookedCapabilities) + builder.append(cookedCapabilities.getCount()); + for (auto& capabilityConjunction : cookedCapabilities) { - builder.append(capability); + builder.append(capabilityConjunction.getExpandedAtoms().getCount()); + for (auto atom : capabilityConjunction.getExpandedAtoms()) + builder.append(atom); } const PassThroughMode passThroughMode = getDownstreamCompilerRequiredForTarget(targetReq->getTarget()); @@ -1523,7 +1526,7 @@ void TargetRequest::setHLSLToVulkanLayoutOptions(HLSLToVulkanLayoutOptions* opts } } -void TargetRequest::addCapability(CapabilityAtom capability) +void TargetRequest::addCapability(CapabilityName capability) { rawCapabilities.add(capability); cookedCapabilities = CapabilitySet::makeEmpty(); @@ -1553,23 +1556,26 @@ CapabilitySet TargetRequest::getTargetCaps() // atoms, so that most of the information about what operations // are available where can be directly encoded on the declarations. - List<CapabilityAtom> atoms; + List<CapabilityName> atoms; + bool isGLSLTarget = false; switch(format) { case CodeGenTarget::GLSL: case CodeGenTarget::GLSL_Vulkan: case CodeGenTarget::GLSL_Vulkan_OneDesc: - atoms.add(CapabilityAtom::GLSL); + isGLSLTarget = true; + atoms.add(CapabilityName::glsl); break; case CodeGenTarget::SPIRV: case CodeGenTarget::SPIRVAssembly: if (targetFlags & SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY) { - atoms.add(CapabilityAtom::SPIRV_DIRECT); + atoms.add(CapabilityName::spirv_1_5); } else { - atoms.add(CapabilityAtom::GLSL); + isGLSLTarget = true; + atoms.add(CapabilityName::glsl); } break; @@ -1578,11 +1584,11 @@ CapabilitySet TargetRequest::getTargetCaps() case CodeGenTarget::DXBytecodeAssembly: case CodeGenTarget::DXIL: case CodeGenTarget::DXILAssembly: - atoms.add(CapabilityAtom::HLSL); + atoms.add(CapabilityName::hlsl); break; case CodeGenTarget::CSource: - atoms.add(CapabilityAtom::C); + atoms.add(CapabilityName::c); break; case CodeGenTarget::CPPSource: @@ -1591,19 +1597,35 @@ CapabilitySet TargetRequest::getTargetCaps() case CodeGenTarget::ShaderSharedLibrary: case CodeGenTarget::HostHostCallable: case CodeGenTarget::ShaderHostCallable: - atoms.add(CapabilityAtom::CPP); + atoms.add(CapabilityName::cpp); break; case CodeGenTarget::CUDASource: case CodeGenTarget::PTX: - atoms.add(CapabilityAtom::CUDA); + atoms.add(CapabilityName::cuda); break; default: break; } - for(auto atom : rawCapabilities) + + CapabilitySet latestSpirvCapSet = CapabilitySet(CapabilityName::spirv_latest); + CapabilityName latestSpirvAtom = (CapabilityName)latestSpirvCapSet.getExpandedAtoms()[0].getExpandedAtoms().getLast(); + for (auto atom : rawCapabilities) + { + if (isGLSLTarget) + { + // If we are emitting GLSL code, we need to + // translate all spirv_*_* capabilities to + // glsl_spirv_*_* instead. + // + if (atom >= CapabilityName::spirv_1_0 && atom <= latestSpirvAtom) + { + atom = (CapabilityName)((Int)CapabilityName::glsl_spirv_1_0 + ((Int)atom - (Int)CapabilityName::spirv_1_0)); + } + } atoms.add(atom); + } cookedCapabilities = CapabilitySet(atoms); return cookedCapabilities; @@ -5088,7 +5110,7 @@ SlangResult EndToEndCompileRequest::addTargetCapability(SlangInt targetIndex, Sl auto& targets = getLinkage()->targets; if(targetIndex < 0 || targetIndex >= targets.getCount()) return SLANG_E_INVALID_ARG; - targets[targetIndex]->addCapability(CapabilityAtom(capability)); + targets[targetIndex]->addCapability(CapabilityName(capability)); return SLANG_OK; } diff --git a/tools/slang-capability-generator/capability-generator-main.cpp b/tools/slang-capability-generator/capability-generator-main.cpp new file mode 100644 index 000000000..79582a8c5 --- /dev/null +++ b/tools/slang-capability-generator/capability-generator-main.cpp @@ -0,0 +1,674 @@ +// capabilities-generator-main.cpp + +#include <stdio.h> +#include "../../source/compiler-core/slang-lexer.h" +#include "../../source/compiler-core/slang-perfect-hash-codegen.h" +#include "../../source/core/slang-io.h" +#include "../../source/core/slang-secure-crt.h" +#include "../../source/core/slang-string-util.h" +#include "../../source/core/slang-file-system.h" + +using namespace Slang; + +namespace Diagnostics +{ +#define DIAGNOSTIC(id, severity, name, messageFormat) const DiagnosticInfo name = { id, Severity::severity, #name, messageFormat }; +#include "slang-capability-diagnostic-defs.h" +#undef DIAGNOSTIC +} + +enum class CapabilityFlavor +{ + Normal, + Abstract, + Alias +}; + +struct CapabilityDef; + +struct CapabilityConjunctionExpr +{ + List<CapabilityDef*> atoms; +}; + +struct CapabilityDisjunctionExpr +{ + List<CapabilityConjunctionExpr> conjunctions; +}; + +struct SerializedArrayView +{ + Index first; + Index count; +}; + +struct CapabilityDef : public RefObject +{ + Index enumValue; + String name; + CapabilityDisjunctionExpr expr; + CapabilityFlavor flavor; + int rank; + List<List<CapabilityDef*>> canonicalRepresentation; + SerializedArrayView serializedCanonicalRepresentation; + + CapabilityDef* getAbstractBase() + { + if (flavor != CapabilityFlavor::Normal) + return nullptr; + if (expr.conjunctions.getCount() != 1) + return nullptr; + if (expr.conjunctions[0].atoms.getCount() == 0) + return nullptr; + if (expr.conjunctions[0].atoms[0]->flavor != CapabilityFlavor::Abstract) + return nullptr; + return expr.conjunctions[0].atoms[0]; + } +}; + +struct CapabilityDefParser +{ + CapabilityDefParser(Lexer* lexer, DiagnosticSink* sink) + : m_lexer(lexer) + , m_sink(sink) + { + } + + Lexer* m_lexer; + DiagnosticSink* m_sink; + + Dictionary<String, CapabilityDef*> m_mapNameToCapability; + List<RefPtr<CapabilityDef>> m_defs; + + TokenReader m_tokenReader; + + bool advanceIf(TokenType type) + { + if (m_tokenReader.peekTokenType() == type) + { + m_tokenReader.advanceToken(); + return true; + } + return false; + } + + SlangResult readToken(TokenType type, Token& nextToken) + { + nextToken = m_tokenReader.advanceToken(); + if (nextToken.type != type) + { + m_sink->diagnose(nextToken.loc, Diagnostics::unexpectedTokenExpectedTokenType, nextToken, type); + return SLANG_FAIL; + } + return SLANG_OK; + } + + SlangResult readToken(TokenType type) + { + Token nextToken; + return readToken(type, nextToken); + } + + SlangResult parseConjunction(CapabilityConjunctionExpr& expr) + { + for (;;) + { + Token nameToken; + SLANG_RETURN_ON_FAIL(readToken(TokenType::Identifier, nameToken)); + CapabilityDef* def = nullptr; + if (m_mapNameToCapability.tryGetValue(nameToken.getContent(), def)) + { + expr.atoms.add(def); + } + else + { + m_sink->diagnose(nameToken.loc, Diagnostics::undefinedIdentifier, nameToken); + return SLANG_FAIL; + } + if (!(advanceIf(TokenType::OpAnd) || advanceIf(TokenType::OpAdd))) + break; + } + return SLANG_OK; + } + + SlangResult parseExpr(CapabilityDisjunctionExpr& expr) + { + for (;;) + { + CapabilityConjunctionExpr conjunction; + SLANG_RETURN_ON_FAIL(parseConjunction(conjunction)); + expr.conjunctions.add(conjunction); + if (!advanceIf(TokenType::OpBitOr)) + break; + } + return SLANG_OK; + } + + SlangResult parseDefs() + { + auto tokens = m_lexer->lexAllSemanticTokens(); + m_tokenReader = TokenReader(tokens); + for (;;) + { + RefPtr<CapabilityDef> def = new CapabilityDef(); + def->flavor = CapabilityFlavor::Normal; + auto nextToken = m_tokenReader.advanceToken(); + if (nextToken.getContent() == "alias") + { + def->flavor = CapabilityFlavor::Alias; + } + else if (nextToken.getContent() == "abstract") + { + def->flavor = CapabilityFlavor::Abstract; + } + else if (nextToken.getContent() == "def") + { + def->flavor = CapabilityFlavor::Normal; + } + else if (nextToken.type == TokenType::EndOfFile) + { + break; + } + else + { + m_sink->diagnose(nextToken.loc, Diagnostics::unexpectedToken, nextToken); + return SLANG_FAIL; + } + + Token nameToken; + SLANG_RETURN_ON_FAIL(readToken(TokenType::Identifier, nameToken)); + def->name = nameToken.getContent(); + + if (def->flavor == CapabilityFlavor::Normal) + { + if (advanceIf(TokenType::Colon)) + { + SLANG_RETURN_ON_FAIL(parseExpr(def->expr)); + } + if (advanceIf(TokenType::OpAssign)) + { + Token rankToken; + SLANG_RETURN_ON_FAIL(readToken(TokenType::IntegerLiteral, rankToken)); + def->rank = stringToInt(rankToken.getContent()); + } + } + else if (def->flavor == CapabilityFlavor::Alias) + { + SLANG_RETURN_ON_FAIL(readToken(TokenType::OpAssign)); + SLANG_RETURN_ON_FAIL(parseExpr(def->expr)); + } + else if (def->flavor == CapabilityFlavor::Abstract) + { + if (advanceIf(TokenType::Colon)) + { + SLANG_RETURN_ON_FAIL(parseExpr(def->expr)); + } + } + SLANG_RETURN_ON_FAIL(readToken(TokenType::Semicolon)); + m_defs.add(def); + if (!m_mapNameToCapability.addIfNotExists(def->name, def)) + { + m_sink->diagnose(nextToken.loc, Diagnostics::redefinition, def->name); + return SLANG_FAIL; + } + } + return SLANG_OK; + } +}; + +struct CapabilityConjunction +{ + HashSet<CapabilityDef*> atoms; + bool implies(const CapabilityConjunction& c) const + { + for (auto& atom : c.atoms) + { + if (!atoms.contains(atom)) + return false; + } + return true; + } + + bool isImpossible() const + { + // Keep a map from an abstract base to the concrete atom defined in this conjunction that implements the base. + Dictionary<CapabilityDef*, CapabilityDef*> abstractKV; + + for (auto& atom : atoms) + { + auto abstractBase = atom->getAbstractBase(); + if (!abstractBase) + continue; + + // Have we already seen another concrete atom that implements the same abstract base of the current atom? + // If so, we have a conflict and the conjunction is impossible. + // + CapabilityDef* value = nullptr; + if (abstractKV.tryGetValue(abstractBase, value)) + { + if (value != atom) + return true; + } + else + { + abstractKV[abstractBase] = atom; + } + } + return false; + } +}; + +struct CapabilityDisjunction +{ + List<CapabilityConjunction> conjunctions; + + void addConjunction(const CapabilityConjunction& c) + { + if (c.isImpossible()) + return; + for (auto& conjunction : conjunctions) + { + if (c.implies(conjunction)) + return; + } + for (Index i = 0; i < conjunctions.getCount();) + { + if (conjunctions[i].implies(c)) + { + conjunctions.fastRemoveAt(i); + } + else + { + i++; + } + } + conjunctions.add(_Move(c)); + } + + CapabilityDisjunction joinWith(const CapabilityDisjunction& other) + { + if (conjunctions.getCount() == 0) + { + return other; + } + if (other.conjunctions.getCount() == 0) + { + return *this; + } + + CapabilityDisjunction result; + + for (auto& thisC : conjunctions) + { + for (auto& thatC : other.conjunctions) + { + CapabilityConjunction newC; + for (auto atom : thisC.atoms) + newC.atoms.add(atom); + for (auto atom : thatC.atoms) + newC.atoms.add(atom); + result.addConjunction(_Move(newC)); + } + } + return result; + } + + List<List<CapabilityDef*>> canonicalize() + { + List<List<CapabilityDef*>> result; + for (auto& c : conjunctions) + { + List<CapabilityDef*> atoms; + for (auto& atom : c.atoms) + atoms.add(atom); + atoms.sort([](CapabilityDef* c1, CapabilityDef* c2) {return c1->enumValue < c2->enumValue; }); + result.add(_Move(atoms)); + } + result.sort([](const List<CapabilityDef*>& c1, const List<CapabilityDef*>& c2) + { + for (Index i = 0; i < Math::Min(c1.getCount(), c2.getCount()); i++) + { + if (c1[i]->enumValue < c2[i]->enumValue) + return true; + else if (c1[i]->enumValue > c2[i]->enumValue) + return false; + } + return c1.getCount() < c2.getCount(); + }); + return result; + } +}; + +CapabilityDisjunction getCanonicalRepresentation(CapabilityDef* def) +{ + CapabilityDisjunction result; + for (auto& c : def->canonicalRepresentation) + { + CapabilityConjunction conj; + for (auto& atom : c) + conj.atoms.add(atom); + result.conjunctions.add(conj); + } + return result; +} + +CapabilityDisjunction evaluateConjunction(const List<CapabilityDef*>& atoms) +{ + CapabilityDisjunction result; + for (auto& def : atoms) + { + CapabilityDisjunction defCanonical = getCanonicalRepresentation(def); + result = result.joinWith(defCanonical); + } + return result; +} + +void calcCanonicalRepresentation(CapabilityDef* def, const List<CapabilityDef*>& mapEnumValueToDef) +{ + CapabilityDisjunction disjunction; + if (def->flavor == CapabilityFlavor::Normal) + { + CapabilityConjunction c; + c.atoms.add(def); + disjunction.conjunctions.add(c); + } + CapabilityDisjunction exprVal; + for (auto& c : def->expr.conjunctions) + { + CapabilityDisjunction evalD = evaluateConjunction(c.atoms); + for (auto& cc : evalD.conjunctions) + exprVal.addConjunction(cc); + } + disjunction = disjunction.joinWith(exprVal); + def->canonicalRepresentation = disjunction.canonicalize(); +} + +void calcCanonicalRepresentations(const List<RefPtr<CapabilityDef>>& defs, const List<CapabilityDef*>& mapEnumValueToDef) +{ + for (auto def : defs) + calcCanonicalRepresentation(def, mapEnumValueToDef); +} + +SlangResult generateDefinitions(const List<RefPtr<CapabilityDef>>& defs, StringBuilder& sbHeader, StringBuilder& sbCpp) +{ + sbHeader << "enum class CapabilityAtom\n{\n"; + sbHeader << " Invalid,\n"; + for (auto def : defs) + { + if (def->flavor == CapabilityFlavor::Normal) + { + sbHeader << " " << def->name << ",\n"; + } + } + sbHeader << " Count\n"; + sbHeader << "};\n"; + CapabilityDef* firstAbstractDef = nullptr; + CapabilityDef* firstAliasDef = nullptr; + sbHeader << "enum class CapabilityName\n{\n"; + sbHeader << " Invalid,\n"; + Index enumValueCounter = 1; + List<CapabilityDef*> mapEnumValueToDef; + mapEnumValueToDef.add(nullptr); // For Invalid. + for (auto def : defs) + { + if (def->flavor == CapabilityFlavor::Normal) + { + def->enumValue = enumValueCounter; + ++enumValueCounter; + mapEnumValueToDef.add(def); + sbHeader << " " << def->name << " = (int)CapabilityAtom::" << def->name << ",\n"; + } + } + for (auto def : defs) + { + if (def->flavor == CapabilityFlavor::Abstract) + { + if (firstAbstractDef == nullptr) + firstAbstractDef = def; + def->enumValue = enumValueCounter; + ++enumValueCounter; + mapEnumValueToDef.add(def); + sbHeader << " " << def->name << ",\n"; + } + } + for (auto def : defs) + { + if (def->flavor == CapabilityFlavor::Alias) + { + if (firstAliasDef == nullptr) + firstAliasDef = def; + def->enumValue = enumValueCounter; + ++enumValueCounter; + mapEnumValueToDef.add(def); + sbHeader << " " << def->name << ",\n"; + } + } + sbHeader << " Count\n"; + sbHeader << "};\n"; + + calcCanonicalRepresentations(defs, mapEnumValueToDef); + + List<String> capabiltiyNameArray; + List<SerializedArrayView> serializedCapabilityArrays; + + List<SerializedArrayView> serializedAtomDisjunctions; + auto serializeConjunction = [&](const List<CapabilityDef*>& capabilities) -> SerializedArrayView + { + // Do we already have a serialized capability array that is the same the one we are trying to serialize? + for (auto existingArray : serializedCapabilityArrays) + { + if (existingArray.count == capabilities.getCount()) + { + bool match = true; + for (Index i = 0; i < capabilities.getCount(); i++) + { + if (capabiltiyNameArray[existingArray.first+i] != capabilities[i]->name) + { + match = false; + break; + } + } + if (match) + return existingArray; + } + } + SerializedArrayView result; + result.first = capabiltiyNameArray.getCount(); + for (auto capability : capabilities) + { + capabiltiyNameArray.add(capability->name); + } + result.count = capabilities.getCount(); + serializedCapabilityArrays.add(result); + return result; + }; + auto serializeDisjunction = [&](const List<SerializedArrayView>& conjunctions) -> SerializedArrayView + { + SerializedArrayView result; + result.first = serializedAtomDisjunctions.getCount(); + for (auto c : conjunctions) + { + serializedAtomDisjunctions.add(c); + } + result.count = conjunctions.getCount(); + return result; + }; + for (auto& def : defs) + { + List<SerializedArrayView> conjunctions; + for (auto& c : def->canonicalRepresentation) + conjunctions.add(serializeConjunction(c)); + def->serializedCanonicalRepresentation = serializeDisjunction(conjunctions); + } + + sbCpp << "static CapabilityName kCapabilityArray[] = {\n"; + Index arrayIndex = 0; + sbCpp << " /* [0] @0: */ "; + for (Index i = 0; i < capabiltiyNameArray.getCount(); ++i) + { + sbCpp << " CapabilityName::" << capabiltiyNameArray[i] << ","; + if (i + 1 == serializedCapabilityArrays[arrayIndex].first + serializedCapabilityArrays[arrayIndex].count) + { + arrayIndex++; + if (arrayIndex == serializedCapabilityArrays.getCount()) + sbCpp << "\n"; + else + sbCpp << "\n /* [" << arrayIndex << "] @" << serializedCapabilityArrays[arrayIndex].first <<": */ "; + } + } + sbCpp << "};\n"; + sbCpp << "static ArrayView<CapabilityName> kCapabilityConjunctions[] = {\n"; + for (auto c : serializedAtomDisjunctions) + { + sbCpp << " { kCapabilityArray + " << c.first << ", " << c.count << " },\n"; + } + sbCpp << "};\n"; + + sbCpp << "static const CapabilityAtomInfo kCapabilityNameInfos[int(CapabilityName::Count)] = {\n"; + for (auto def : mapEnumValueToDef) + { + if (!def) + { + sbCpp << R"( { "Invalid", CapabilityNameFlavor::Concrete, CapabilityName::Invalid, 0, {nullptr, 0} },)" << "\n"; + continue; + } + + // name. + sbCpp << " { \"" << def->name << "\", "; + + // flavor. + switch (def->flavor) + { + case CapabilityFlavor::Normal: + sbCpp << "CapabilityNameFlavor::Concrete"; + break; + case CapabilityFlavor::Abstract: + sbCpp << "CapabilityNameFlavor::Abstract"; + break; + case CapabilityFlavor::Alias: + sbCpp << "CapabilityNameFlavor::Alias"; + break; + } + sbCpp << ", "; + + // abstract base. + auto abstractBase = def->getAbstractBase(); + if (abstractBase) + { + sbCpp << "CapabilityName::" << abstractBase->name; + } + else + { + sbCpp << "CapabilityName::Invalid"; + } + sbCpp << ", "; + + // canonnical representation. + sbCpp << def->rank << ", { kCapabilityConjunctions + " << def->serializedCanonicalRepresentation.first << ", " << def->serializedCanonicalRepresentation.count << "} },\n"; + } + + sbCpp << "};\n"; + return SLANG_OK; +} + + +SlangResult parseDefFile(DiagnosticSink* sink, String inputPath, List<RefPtr<CapabilityDef>>& outDefs) +{ + auto sourceManager = sink->getSourceManager(); + + String contents; + SLANG_RETURN_ON_FAIL(File::readAllText(inputPath, contents)); + PathInfo pathInfo = PathInfo::makeFromString(inputPath); + SourceFile* sourceFile = sourceManager->createSourceFileWithString(pathInfo, contents); + SourceView* sourceView = sourceManager->createSourceView(sourceFile, nullptr, SourceLoc()); + Lexer lexer; + NamePool namePool; + RootNamePool rootPool; + namePool.setRootNamePool(&rootPool); + lexer.initialize(sourceView, sink, &namePool, sourceManager->getMemoryArena()); + + CapabilityDefParser parser(&lexer, sink); + + SLANG_RETURN_ON_FAIL(parser.parseDefs()); + outDefs = _Move(parser.m_defs); + return SLANG_OK; +} + +void printDiagnostics(DiagnosticSink* sink) +{ + ComPtr<ISlangBlob> blob; + sink->getBlobIfNeeded(blob.writeRef()); + if (blob) + { + fprintf(stderr, "%s", (const char*)blob->getBufferPointer()); + } +} + +void writeIfChanged(String fileName, String content) +{ + if (File::exists(fileName)) + { + String existingContent; + File::readAllText(fileName, existingContent); + if (existingContent.getUnownedSlice().trim() == content.getUnownedSlice().trim()) + return; + } + File::writeAllText(fileName, content); +} + +int main(int argc, const char* const* argv) +{ + if (argc < 2) + { + fprintf( + stderr, + "Usage: %s\n", + argc >= 1 ? argv[0] : "slang-capabilities-generator"); + return 1; + } + String targetDir; + for (int i = 0; i < argc - 1; i++) + { + if (strcmp(argv[i], "--target-directory") == 0) + targetDir = argv[i + 1]; + } + + String inPath = argv[1]; + if (targetDir.getLength() == 0) + targetDir = Path::getParentDirectory(inPath); + + auto outCppPath = Path::combine(targetDir, "slang-generated-capability-defs-impl.h"); + auto outHeaderPath = Path::combine(targetDir, "slang-generated-capability-defs.h"); + auto outLookupPath = Path::combine(targetDir, "slang-lookup-capability-defs.cpp"); + SourceManager sourceManager; + sourceManager.initialize(nullptr, OSFileSystem::getExtSingleton()); + DiagnosticSink sink(&sourceManager, nullptr); + List<RefPtr<CapabilityDef>> defs; + if (SLANG_FAILED(parseDefFile(&sink, inPath, defs))) + { + printDiagnostics(&sink); + return 1; + } + + StringBuilder sbHeader, sbCpp; + if (SLANG_FAILED(generateDefinitions(defs, sbHeader, sbCpp))) + { + return 1; + } + + writeIfChanged(outHeaderPath, sbHeader.produceString()); + writeIfChanged(outCppPath, sbCpp.produceString()); + + List<String> opnames; + for (auto def : defs) + { + opnames.add(def->name); + } + + if (SLANG_FAILED(writePerfectHashLookupCppFile(outLookupPath, opnames, "CapabilityName", "CapabilityName::", "slang-capability.h", &sink))) + { + printDiagnostics(&sink); + return 1; + } + return 0; +} diff --git a/tools/slang-capability-generator/slang-capability-diagnostic-defs.h b/tools/slang-capability-generator/slang-capability-diagnostic-defs.h new file mode 100644 index 000000000..a9436cd9c --- /dev/null +++ b/tools/slang-capability-generator/slang-capability-diagnostic-defs.h @@ -0,0 +1,57 @@ +// + +// The file is meant to be included multiple times, to produce different +// pieces of declaration/definition code related to diagnostic messages +// +// Each diagnostic is declared here with: +// +// DIAGNOSTIC(id, severity, name, messageFormat) +// +// Where `id` is the unique diagnostic ID, `severity` is the default +// severity (from the `Severity` enum), `name` is a name used to refer +// to this diagnostic from code, and `messageFormat` is the default +// (non-localized) message for the diagnostic, with placeholders +// for any arguments. + +#ifndef DIAGNOSTIC +#error Need to #define DIAGNOSTIC(...) before including "DiagnosticDefs.h" +#define DIAGNOSTIC(id, severity, name, messageFormat) /* */ +#endif + +// +// -1 - Notes that decorate another diagnostic. +// + +DIAGNOSTIC(-1, Note, seeDefinitionOf, "see definition of '$0'") + +// +// 0xxxx - Command line and interaction with host platform APIs. +// + +DIAGNOSTIC( 1, Error, cannotOpenFile, "cannot open file '$0'.") +DIAGNOSTIC( 2, Error, cannotFindFile, "cannot find file '$0'.") +DIAGNOSTIC( 4, Error, cannotWriteOutputFile, "cannot write output file '$0'.") +DIAGNOSTIC( 5, Error, failedToLoadDynamicLibrary, "failed to load dynamic library '$0'") +DIAGNOSTIC( 6, Error, tooManyOutputPathsSpecified, "$0 output paths specified, but only $1 entry points given") + +// +// 2xxxx - Parsing +// + +DIAGNOSTIC(20003, Error, unexpectedToken, "unexpected $0") +DIAGNOSTIC(20001, Error, unexpectedTokenExpectedTokenType, "unexpected $0, expected $1") +DIAGNOSTIC(20001, Error, unexpectedTokenExpectedTokenName, "unexpected $0, expected '$1'") + +DIAGNOSTIC(0, Error, tokenNameExpectedButEOF, "\"$0\" expected but end of file encountered.") +DIAGNOSTIC(0, Error, tokenTypeExpectedButEOF, "$0 expected but end of file encountered.") +DIAGNOSTIC(20001, Error, tokenNameExpected, "\"$0\" expected") +DIAGNOSTIC(20001, Error, tokenNameExpectedButEOF2, "\"$0\" expected but end of file encountered.") +DIAGNOSTIC(20001, Error, tokenTypeExpected, "$0 expected") +DIAGNOSTIC(20001, Error, tokenTypeExpectedButEOF2, "$0 expected but end of file encountered.") +DIAGNOSTIC(20001, Error, typeNameExpectedBut, "unexpected $0, expected type name") +DIAGNOSTIC(20001, Error, typeNameExpectedButEOF, "type name expected but end of file encountered.") +DIAGNOSTIC(20001, Error, unexpectedEOF, " Unexpected end of file.") +DIAGNOSTIC(20002, Error, syntaxError, "syntax error.") +DIAGNOSTIC(20003, Error, undefinedIdentifier, "undefined identifier \"$0\".") +DIAGNOSTIC(20004, Error, redefinition, "capability redefinition: '$0'.") +#undef DIAGNOSTIC diff --git a/tools/slang-lookup-generator/lookup-generator-main.cpp b/tools/slang-lookup-generator/lookup-generator-main.cpp index a6f43c102..b99cf0e53 100644 --- a/tools/slang-lookup-generator/lookup-generator-main.cpp +++ b/tools/slang-lookup-generator/lookup-generator-main.cpp @@ -4,7 +4,7 @@ #include "../../source/compiler-core/slang-json-parser.h" #include "../../source/compiler-core/slang-json-value.h" #include "../../source/compiler-core/slang-lexer.h" -#include "../../source/compiler-core/slang-perfect-hash.h" +#include "../../source/compiler-core/slang-perfect-hash-codegen.h" #include "../../source/core/slang-io.h" #include "../../source/core/slang-secure-crt.h" #include "../../source/core/slang-string-util.h" @@ -69,46 +69,6 @@ static List<String> extractOpNames(UnownedStringSlice& error, const JSONValue& v return opnames; } -void writeHashFile( - const char* const outCppPath, - const char* valueType, - const char* valuePrefix, - const List<String> includes, - const HashParams& hashParams, - const List<String> values) -{ - StringBuilder sb; - StringWriter writer(&sb, WriterFlags(0)); - WriterHelper w(&writer); - - w.print("// Hash function for %s\n", valueType); - w.print("//\n"); - w.print("// This file was thoughtfully generated by a machine,\n"); - w.print("// don't even think about modifying it yourself!\n"); - w.print("//\n"); - w.print("\n"); - for (const auto& i : includes) - { - w.print("#include \"%s\"\n", i.getBuffer()); - } - w.print("\n"); - w.print("\n"); - w.print("namespace Slang\n"); - w.print("{\n"); - w.print("\n"); - - w.put(perfectHashToEmbeddableCpp( - hashParams, - UnownedStringSlice(valueType), - (String("lookup") + valueType).getUnownedSlice(), - values - ).getBuffer()); - - w.print("}\n"); - - File::writeAllTextIfChanged(outCppPath, sb.getUnownedSlice()); -} - int main(int argc, const char* const* argv) { using namespace Slang; @@ -166,38 +126,8 @@ int main(int argc, const char* const* argv) opnames.add(w); } - HashParams hashParams; - auto r = minimalPerfectHash(opnames, hashParams); - switch (r) - { - case HashFindResult::UnavoidableHashCollision: - { - sink.diagnoseRaw( - Severity::Error, - "Unable to find a non-overlapping hash function.\n" - "The hash function probably has a unavoidable " - "collision for some input words\n"); - return 1; - } - case HashFindResult::NonUniqueKeys: - { - sink.diagnoseRaw(Severity::Error, "Input word list has duplicates\n"); - return 1; - } - case HashFindResult::Success:; - } - - List<String> values; - values.reserve (hashParams.destTable.getCount()); - for(const auto& v : hashParams.destTable) - values.add(enumerantPrefix + v); - writeHashFile( - outCppPath, - enumName, - enumerantPrefix, - { "../core/slang-common.h", "../core/slang-string.h", enumHeader }, - hashParams, - values); + if (SLANG_FAILED(writePerfectHashLookupCppFile(outCppPath, opnames, enumName, enumerantPrefix, enumHeader, &sink))) + return -1; return 0; } |
