summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt5
-rw-r--r--build/visual-studio/compiler-core/compiler-core.vcxproj2
-rw-r--r--build/visual-studio/compiler-core/compiler-core.vcxproj.filters6
-rw-r--r--build/visual-studio/run-generators/run-generators.vcxproj17
-rw-r--r--build/visual-studio/run-generators/run-generators.vcxproj.filters9
-rw-r--r--build/visual-studio/slang/slang.vcxproj4
-rw-r--r--build/visual-studio/slang/slang.vcxproj.filters12
-rw-r--r--premake5.lua37
-rw-r--r--slang.sln16
-rw-r--r--source/compiler-core/slang-perfect-hash-codegen.cpp81
-rw-r--r--source/compiler-core/slang-perfect-hash-codegen.h9
-rw-r--r--source/compiler-core/slang-perfect-hash.cpp2
-rw-r--r--source/slang/CMakeLists.txt52
-rw-r--r--source/slang/hlsl.meta.slang90
-rw-r--r--source/slang/slang-capabilities.capdef135
-rw-r--r--source/slang/slang-capability-defs.h103
-rw-r--r--source/slang/slang-capability.cpp703
-rw-r--r--source/slang/slang-capability.h141
-rw-r--r--source/slang/slang-compiler.cpp26
-rwxr-xr-xsource/slang/slang-compiler.h4
-rw-r--r--source/slang/slang-doc-markdown-writer.cpp23
-rw-r--r--source/slang/slang-emit-c-like.cpp3
-rw-r--r--source/slang/slang-emit-glsl.cpp18
-rw-r--r--source/slang/slang-emit-spirv.cpp2
-rw-r--r--source/slang/slang-ir-dll-import.cpp2
-rw-r--r--source/slang/slang-ir-glsl-legalize.cpp4
-rw-r--r--source/slang/slang-ir-inst-defs.h4
-rw-r--r--source/slang/slang-ir-insts.h4
-rw-r--r--source/slang/slang-ir-specialize-target-switch.cpp4
-rw-r--r--source/slang/slang-ir.cpp58
-rw-r--r--source/slang/slang-lower-to-ir.cpp8
-rw-r--r--source/slang/slang-options.cpp21
-rw-r--r--source/slang/slang-parser.cpp4
-rw-r--r--source/slang/slang.cpp50
-rw-r--r--tools/slang-capability-generator/capability-generator-main.cpp674
-rw-r--r--tools/slang-capability-generator/slang-capability-diagnostic-defs.h57
-rw-r--r--tools/slang-lookup-generator/lookup-generator-main.cpp76
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
diff --git a/slang.sln b/slang.sln
index c547b4e67..d43d1fe81 100644
--- a/slang.sln
+++ b/slang.sln
@@ -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;
}