From c5c1a25ab6d0e509e893d737a679ac47949df2f6 Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 18 Jan 2024 16:46:00 -0800 Subject: Capability def parsing & codegen + disjoint sets (#3451) * Capability def parsing & codegen + disjoint sets This change adds a capability definition file, and a code generator to produce C++ code that defines the capability enums and necessary data structures around the capabilities. Extends the existing CapabilitySet class to support expressing disjoint sets of capabilities. This sets up for the next change that will enhance our type checking with reasoning of capability requirements. * Fix cmake. * Fix warning. * Fix. * Fix isBetterForTarget to prefer less specialized option. * Fix. * Fix premake. * Fix intrinsic. * Fix vs sln file. --------- Co-authored-by: Yong He --- .../compiler-core/slang-perfect-hash-codegen.cpp | 81 +++ source/compiler-core/slang-perfect-hash-codegen.h | 9 + source/compiler-core/slang-perfect-hash.cpp | 2 +- source/slang/CMakeLists.txt | 52 ++ source/slang/hlsl.meta.slang | 90 +-- source/slang/slang-capabilities.capdef | 135 ++++ source/slang/slang-capability-defs.h | 103 --- source/slang/slang-capability.cpp | 703 +++++++++++++++------ source/slang/slang-capability.h | 141 ++++- source/slang/slang-compiler.cpp | 26 +- source/slang/slang-compiler.h | 4 +- source/slang/slang-doc-markdown-writer.cpp | 23 +- source/slang/slang-emit-c-like.cpp | 3 +- source/slang/slang-emit-glsl.cpp | 18 +- source/slang/slang-emit-spirv.cpp | 2 +- source/slang/slang-ir-dll-import.cpp | 2 +- source/slang/slang-ir-glsl-legalize.cpp | 4 +- source/slang/slang-ir-inst-defs.h | 4 +- source/slang/slang-ir-insts.h | 4 +- source/slang/slang-ir-specialize-target-switch.cpp | 4 +- source/slang/slang-ir.cpp | 58 +- source/slang/slang-lower-to-ir.cpp | 8 +- source/slang/slang-options.cpp | 21 +- source/slang/slang-parser.cpp | 4 +- source/slang/slang.cpp | 50 +- 25 files changed, 1070 insertions(+), 481 deletions(-) create mode 100644 source/compiler-core/slang-perfect-hash-codegen.cpp create mode 100644 source/compiler-core/slang-perfect-hash-codegen.h create mode 100644 source/slang/slang-capabilities.capdef delete mode 100644 source/slang/slang-capability-defs.h (limited to 'source') 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 includes, + const HashParams& hashParams, + const List 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 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 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 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 @@ -46,6 +46,54 @@ target_include_directories( INTERFACE ${SLANG_STDLIB_META_OUTPUT_DIR} ) +# +# 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 GetAttributeAtVertex(vector 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 GetAttributeAtVertex(matrix 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> 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& ioNames) +void getCapabilityNames(List& 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 const& atoms) +CapabilityConjunctionSet::CapabilityConjunctionSet(List 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& 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 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& outAtoms) const +void CapabilityConjunctionSet::calcCompactedAtoms(List& 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& 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& 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 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>& outAtoms) const +{ + for (auto& c : m_conjunctions) + { + List 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 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 thisSets = m_conjunctions.getArrayView(); + ArrayView 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 set, List& 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 viableConjunctionsThis; + List 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 const& atoms); + explicit CapabilityConjunctionSet(List 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 const& getExpandedAtoms() const { return m_expandedAtoms; } + List& 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& 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 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& 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& getExpandedAtoms() { return m_conjunctions; } + const List& 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>& outAtoms) const; + + bool isBetterForTarget(CapabilitySet const& that, CapabilitySet const& targetCaps) const; + +private: + // The underlying representation we use is a list of conjunctions. + // + List 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& ioNames); +void getCapabilityNames(List& 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 rawCapabilities; + List 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 atoms; - - Index count = (Index) getOperandCount(); - for(Index i = 0; i < count; ++i) + switch (getOp()) { - auto operand = cast(getOperand(i)); - atoms.add(CapabilityAtom(operand->getValue())); - } + case kIROp_CapabilityConjunction: + { + List atoms; + + Index count = (Index)getOperandCount(); + for (Index i = 0; i < count; ++i) + { + auto operand = cast(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(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 compactedAtoms; + List> compactedAtoms; caps.calcCompactedAtoms(compactedAtoms); - - List args; - for( auto atom : compactedAtoms ) + List conjunctions; + for( auto atomConjunction : compactedAtoms ) { - args.add(getIntValue(capabilityAtomType, Int(atom))); + List 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 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 // 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 } 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 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 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 capabilityAtoms; + List 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(); - 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& 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 atoms; + List 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; } -- cgit v1.2.3